<?php

namespace App\Modules\Bookings;

use Illuminate\Support\Facades\DB;

use App\Modules\Stock\Stock;
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;

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

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

        if ($request->has('stock_id')) {
            $query->where('stock_id', $request->input('stock_id'));
        }

        if ($request->has('status')) {
            $query->where('status', $request->input('status'));
        }

        if ($request->has('related_type')) {
            $query->where('related_type', $request->input('related_type'));
        }

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

        if ($request->input('query')) {
            $query->where('related_id', 'like', '%'.$request->input('query').'%')->orWhereHas('stock', function ($q) {
                $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));

        $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();

        $build = new Build($data);
        $build->status = 'planning';

        // Call the Build Service to create the build, do the logic in the creation and sync with stock if needed
        $service = App::make('App\Modules\Bookings\BuildService');
        
        try {
            if ($build->stock && ! $build->stock->composite) {
                throw new Exception('To create a new build you must select a composite stock product.', 1);
            }
            
            $service->validate($build);
            $build = $service->create($build, $data);
        } catch (Exception $e) {
            return response()->json([
                'success' => false,
                'error' => $e->getMessage()
            ], 400);
        }

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

        $build->load('stock');

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

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     *
     * @return Response
     */
    public function show($id)
    {
        $build = Build::find($id);

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

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

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

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

        if ($build->status == 'completed') {
            throw new Exception('A completed build cannot be modified.', 1);
        }

        // Check to see if the build has been updated since the user first loaded it to not overwrite other's changes
        // Temporarily disabled optimistic locking to debug
        /*
        if ($request->has('updated_at') && $request->input('updated_at') != $build->updated_at->toDateTimeString()) {
            \Log::error('Timestamp mismatch', [
                'request_updated_at' => $request->input('updated_at'),
                'build_updated_at' => $build->updated_at->toDateTimeString(),
                'build_updated_at_raw' => $build->updated_at,
                'request_data' => $request->all()
            ]);
            throw new JobModifiedSinceLastOpenedException();
        }
        */

        // Create a PartService to update the part
        $service = App::make('App\Modules\Bookings\BuildService');
        $service->validate($build);
        
        try {
            $build = $service->update($build, $request->all());
        } catch (Exception $e) {
            return response()->json([
                'errors' => [$e->getMessage()],
            ], 400);
        }

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

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

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     *
     * @return Response
     */
    public function destroy($id)
    {
        $build = Build::find($id);

        if ($build->completed) {
            throw new Exception('A completed build can not be deleted.', 1);
        }

        // Access validations
        // should be implemented here...
        if (! $build) {
            return response()->json(['error' => 'Build not found.'], 404);
        }

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

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

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

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

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

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

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

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

        //Check if the quantity to mark as received is available
        $build = Build::findOrFail($id);
        $component = $build->ordered_components()->where('stock_id', $request->input('stock_id'))->first();

        if ($component->quantity < $quantity) {
            return response()->json(['errors' => ['In stock there '.($quantity > 1 ? 'are' : 'is').' not '.$quantity.' '.$component->name.' available. The quantity available is '.$stock->quantity.'.']], 400);
        }

        if (! $component) {
            return response()->json(['errors' => ['Component not found.']], 400);
        }

        if ($component->pivot->quantity < $quantity) {
            return response()->json(['errors' => ['The quantity indicated is greater than the quantity needed for this part.']], 400);
        }

        return DB::transaction(function () use (&$component, &$build, $quantity) {

            $build->ordered_components()->detach($component->id);

            if ($component->pivot->quantity > $quantity) {
                $build->ordered_components()->attach([$component->id => ['quantity' => $component->pivot->quantity - $quantity]]);
            }

            Event::dispatch('BuildComponentReceived', [$build, $component, $quantity]);

            return $component;
        });
    }
}
