<?php

namespace App\Modules\Bookings\controllers\api\v1;

use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\DB;

use App\Modules\Stock\Stock;
use App\Modules\Bookings\Part;
use Illuminate\Support\Facades\Event;
use Exception;
use Illuminate\Support\Facades\App;
use App\Exceptions\JobModifiedSinceLastOpenedException;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Models\Status;

class PartsControllerREST extends BaseController
{
    /**
     * Display a listing of the resource.
     *
     * @return Response
     */
    public function index(Request $request)
    {
        $query = Part::query()->select('parts.*')->with('stock');

        if ($request->input('related') && $request->input('related') != 'false') {
            $query->with('related');
        }
        if ($request->has('related_type')) {
            $query->where('related_type', $request->input('related_type'));
        }

        if ($request->input('modifiedSince')) {
            $query->where('parts.updated_at', '>=', $request->input('modifiedSince'));
        }

        if ($request->input('on_order', 0)) {
            $query->where('quantity_on_order', '>', 0)->leftJoin('tasks', 'parts.related_id', '=', 'tasks.id')->where('related_type', 'task')->where('tasks.status_id', '!=', Status::ORDERS_CANCELLED)->where('tasks.status_id', '!=', Status::TASKS_CANCELLED);
        }

        if ($request->input('related_id')) {
            $query->where('related_id', '=', $request->input('related_id'));
        }

        if ($request->input('task_type')) {
            $query->join('tasks', 'tasks.id', '=', 'parts.related_id')->where('parts.related_type', '=', $request->input('type', 'task'))->where('tasks.type', '=', $request->input('task_type'));
        }

        if ($request->input('query')) {
            $query->where('related_id', 'like', '%'.$request->input('query').'%')->orWhereHas('stock', function ($q) use ($request) {
                $q->where('stock.id', 'part.stock_id')->where('stock.name', 'like', '%'.$request->input('query').'%');
            });
        }

        if ($request->input('assigned_to') && $request->input('related_type')) {
            switch ($request->input('related_type')) {
                case 'task':
                    $query->join('tasks', 'tasks.id', '=', 'parts.related_id')->where('parts.related_type', '=', 'task')->where('tasks.assigned_to', '=', $request->input('assigned_to'));
                    break;
            }
        }

        // Apply limits and execute the query
        $query = $query->paginate($request->input('limit', 20));

        // Load all the clients
        // unfortuately eager loading is not available
        // for polymorphic relationships at the moment..
        if ($request->input('client') && $request->input('client') != 'false') {
            foreach ($query as $value) {
                $value->client;
            }
        }

        $response = [
            'success'      => true,
            'data'         => $query->getCollection()->toArray(),
            'total'        => $query->total(),
            'current_page' => $query->currentPage(),
            'last_page'    => $query->lastPage(),
        ];

        return response()->json($response, ($response['success']) ? 200 : 400);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @return Response
     */
    public function store(Request $request)
    {
        $data = $request->all();

        $service = App::make('App\Modules\Bookings\PartService');
        $part = $service->create($data);

        if ($part->exists) {
            return response()->json($part->toArray());
        }

        return response()->json([
            'errors' => $part->errors->all(),
        ], 400);
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     *
     * @return Response
     */
    public function show($id)
    {
        $part = Part::query()->find($id);

        $part->load('creator', 'stock', 'stock.order', 'stock.order.status');

        if (! $part) {
            return response()->json(['error' => 'Part not found.'], 404);
        }

        return response()->json($part->toArray());
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  int  $id
     *
     * @return Response
     */
    public function update(Request $request, $id)
    {
        $part = Part::findOrFail($id);

        // Check to see if the part has been updated since the user first loaded it to not overwrite other's changes
        if ($request->has('updated_at')) {
            $requestUpdatedAt = \Carbon\Carbon::parse($request->input('updated_at'))->setTimezone('Europe/London');
            $partUpdatedAt = \Carbon\Carbon::parse($part->updated_at)->setTimezone('Europe/London');
            
            if (!$requestUpdatedAt->equalTo($partUpdatedAt)) {
                throw new JobModifiedSinceLastOpenedException();
            }
        }

        // Create a PartService to update the part
        $partService = App::make('App\Modules\Bookings\PartService');
        $part = $partService->update($part, $request->all());

        if ($part->errors) {
            return response()->json([
                'errors' => $part->errors->all(),
            ], 400);
        }

        return response()->json($part->toArray());
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     *
     * @return Response
     */
    public function destroy($id)
    {
        $part = Part::query()->find($id);
        // Access validations
        // should be implemented here...

        if (! $part) {
            return response()->json(['error' => 'Part not found.'], 404);
        }

        //Avoid delete if part belongs to an invoiced task
        $partService = App::make('App\Modules\Bookings\PartService');
        $partService->validate($part);

        return DB::transaction(function () use ($part) {
            if ($part->delete()) {
                Event::dispatch('PartDeleted', $part);
                return response()->json($part->toArray());
            }

            return response()->json([
                'errors' => $part->errors->all(),
            ], 400);
        });
    }

    /**
     * Request a new part or an existing part.
     *
     * @return Response
     */
    public function requestPart(Request $request)
    {
        $data = $request->all();

        $service = App::make('App\Modules\Bookings\PartRequester');
        $part = $service->request($data);

        if ($part->exists) {
            return response()->json($part->toArray());
        }

        return response()->json([
            'errors' => $part->errors->all(),
        ], 400);
    }

    /**
     * Mark a part as being received.
     *
     * @return Response
     */
    public function received(Request $request, $id)
    {
        $quantity = $request->input('quantity_received');
        $part = Part::findOrFail($id);

        if ($part->stock->quantity < $quantity) {
            throw new Exception('The amount you are trying to assignee to this part is not available.', 1);
        }

        if ($part->quantity_on_order < $quantity) {
            throw new Exception('The amount you are trying to assignee to this part is greater than the amount needed.', 1);
        }

        if ($part->stock->id != $request->input('stock_id')) {
            throw new Exception('The part is not waiting for the stock is being assigned to it.', 1);
        }

        return DB::transaction(function () use (&$part, &$stock, $quantity) {

            $part->quantity_on_order -= $quantity;

            if (! $part->save()) {
                return response()->json(['errors' => $part->errors->all()], 400);
            }

            Event::dispatch('PartReceived', [$part, $part->stock, $quantity]);

            return $part;
        });
    }
}
