<?php

namespace App\Http\Controllers;

use App\Http\Traits\SavedSearchTrait;
use Illuminate\Http\Request;
use Schema;
use Storage;
use App\Models\Accessory;
use App\Models\Location;
use App\Models\TechnicalSpecs;
use Facades\App\Services\AccessoryCalculation;
use Facades\App\Services\SearchAccessories;
use Facades\App\Services\PurchaseFileGenerator;
use Facades\App\Repositories\HardwareStandards as Hard;
use App\User;
use App\Models\SavedSearch;
use Facades\App\Services\Accessory as AccessoryService;
use Carbon\Carbon;
use Exception;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;

class AccessoriesController extends Controller
{

    // Trait that provides functionalities for managing saved searches.
    use SavedSearchTrait;


    /**
     * Display the accessories index page.
     *
     * @return \Illuminate\View\View|\Illuminate\Http\RedirectResponse
     */
    public function index()
    {
        $userId = Auth::id();
        $user = User::with('userType')->find($userId);
        $savedFilters = SavedSearch::reportName('accessory')->userSavedSearch($userId)->get();
        $viewAllFilters = SavedSearch::reportName('accessory')->adminSavedSearch()->get();

        $search = [];
        $savedSearch = [];

        if (request('admin_saved_search') || request('user_saved_search')) {
            try {
                if (request('admin_saved_search')) {
                    $savedSearch = SavedSearch::reportName('accessory')->where('id', request('admin_saved_search'))->where('view_all', 1)->firstOrFail();
                }

                if (request('user_saved_search')) {
                    $savedSearch = SavedSearch::reportName('accessory')->where('id', request('user_saved_search'))->where('user_id', Auth::id())->whereNull('view_all')->firstOrFail();
                }

                $search = AccessoryService::decodeSearch($savedSearch->parameters);
            } catch (Exception $e) {
                return redirect(route('accessories.index'))->with('error', 'Search not found!');
            }
        }

        $hardwareStandards = Hard::getHardwareStandardsAccessories();
        $technicalSpecs = TechnicalSpecs::accessories()->orderBy('details')->get();

        $locations = Location::Warehouses()->active()->orderBy('room_name', 'ASC')->get();

        return view('accessories.index', compact('user', 'savedFilters', 'viewAllFilters', 'savedSearch', 'locations', 'technicalSpecs', 'hardwareStandards', 'search'));
    }


    /**
     * Retrieve and filter accessory data for DataTables.
     *
     * @param \Illuminate\Http\Request $request The incoming request instance.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function data(Request $request)
    {
        $filteredData = AccessoryService::getAccessoriesData();
        $accessories =  $filteredData['accessories'];
        $totalData = $filteredData['count'];
        $totalFiltered = $totalData;

        $start = request('start');
        $data = [];

        if (!empty($accessories)) {
            $data = AccessoryService::getData($accessories, $start, $data);
        }

        $jsonData = [
            "draw"            => intval($request->input('draw')),
            "recordsTotal"    => intval($totalData),
            "recordsFiltered" => intval($totalFiltered),
            "data"            => $data,
            "purchaseFile" => $filteredData['purchaseFile'],
            "purchaseIndicator" => $filteredData['purchaseIndicator'],

        ];

        return response()->json($jsonData);
    }


    /**
     * Store a newly created accessory in the database.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function store()
    {
        $this->validate(request(), [
            'location_id' => 'required',
            'make_and_model_id' => 'required',
            'available_quantity' => 'required|numeric|min:0',
            'min' => 'nullable|numeric|min:0',
            'max' => 'nullable|numeric|min:0',
        ]);

        $checkExists = Accessory::where('location_id', request('location_id'))->where('make_and_model_id', request('make_and_model_id'))->count();

        if ($checkExists > 0) {
            $message = __('error.accessory_exists_location');

            return response()->json(['status' => false, 'message' => $message]);
        }

        $data['make_and_model_id'] = request('make_and_model_id');
        $data['location_id'] = request('location_id');
        $data['available_quantity'] = request('available_quantity');

        Accessory::create($data);

        request()->session()->flash('message', 'Accessories created successfully.');

        $messageView = view('partials.success')->render();

        return response()->json(['status' => true, 'message' => $messageView]);
    }

    /**
     * Move a specified quantity of an accessory to a new location.
     *
     * @param int $accessoryId The ID of the accessory to be moved.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function move($accessoryId)
    {
        $this->validate(request(), [
            'location_id' => 'required',
            'available_quantity' => 'required|numeric|min:0',
        ]);

        $accessory = Accessory::findOrFail($accessoryId);

        if (request('available_quantity') == 0) {
            $message = __('error.quantity_must_be_greater_than_zero');
            return response()->json(['status' => false, 'message' => $message]);
        }

        if (request('available_quantity') > $accessory->available_quantity) {
            $message = __('error.quantity_must_be_less_than_or_equal');
            return response()->json(['status' => false, 'message' => $message]);
        }

        if (!is_null($accessory->max)) {
            if (request('available_quantity') > $accessory->max) {
                $message = __('error.maximum_value_exceeded');
                return response()->json(['status' => false, 'message' => $message]);
            }
        }

        $data = [
            'location_id' => request('location_id'),
            'make_and_model_id' => $accessory->make_and_model_id,
        ];

        // Reduce the number of locations from the existing row.
        $accessory->decrement('available_quantity', request('available_quantity'));

        if ($accessory->available_quantity == 0) {
        } else {
            AccessoryCalculation::updateCalculations($accessory);
        }

        $accessoryTobeMoved = Accessory::where($data);

        // If the accessory exists in the destination location, update the quantity.
        if ($accessoryTobeMoved->count()) {
            $accessoryMoved = $accessoryTobeMoved->first();
            $accessoryTobeMoved->increment('available_quantity', request('available_quantity'));
        } else {

            // If not, create a new row.
            $data['available_quantity'] = request('available_quantity');

            $accessoryMoved = Accessory::create($data);
        }

        AccessoryCalculation::updateCalculations($accessoryMoved);

        request()->session()->flash('message', 'Accessories transferred successfully.');

        $messageView = view('partials.success')->render();

        return response()->json(['status' => true, 'message' => $messageView]);
    }


    /**
     * Add a specified quantity to an accessory's available quantity.
     *
     * @param int $accessoryId The ID of the accessory to be updated.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function add($accessoryId)
    {
        $validator = Validator::make(request()->all(), [
            'available_quantity' => 'required|numeric|gt:0',
        ]);

        if ($validator->fails()) {
            return response()->json(['status' => false, 'message' => 'Invalid quantity']);
        }

        $accessory = Accessory::findOrFail($accessoryId);

        $accessory->increment('available_quantity', request('available_quantity'));

        AccessoryCalculation::updateCalculations($accessory);

        request()->session()->flash('message', 'Accessories added successfully.');

        $messageView = view('partials.success')->render();

        return response()->json(['status' => true, 'message' => $messageView]);
    }


    /**
     * Update the quantity of a specified accessory.
     *
     * @param int $accessoryId The ID of the accessory to be updated.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function updateQuantity($accessoryId)
    {
        $validator = Validator::make(request()->all(), [
            'value' => 'required|numeric|min:0',
            'field' => 'required'
        ]);

        if ($validator->fails()) {
            return response()->json(['status' => false, 'message' => 'Invalid quantity']);
        }

        if (!Schema::hasColumn('accessories', request('field'))) {
            return response()->json(false);
        }

        $accessory = Accessory::findOrFail($accessoryId);

        $accessory->update([
            request('field') => request('value')
        ]);

        $calculationData = AccessoryCalculation::updateCalculations($accessory);

        return response()->json($calculationData);
    }


    /**
     * Consume a specified quantity of an accessory.
     *
     * @param \Illuminate\Http\Request $request   The incoming request instance.
     * @param int                      $accessory The ID of the accessory to be used.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function use(Request $request, $accessory)
    {
        $validator = Validator::make($request->all(), [
            'available_quantity' => 'required|numeric|gt:0',
        ]);

        if ($validator->fails()) {
            return response()->json(['status' => false, 'message' => 'Invalid quantity']);
        }

        $accessory = Accessory::findOrFail($accessory);

        if (request('available_quantity') > $accessory->available_quantity) {
            $message = __('error.quantity_must_be_less_than_or_equal');

            return response()->json(['status' => false, 'message' => $message]);
        }

        $accessory->decrement('available_quantity', request('available_quantity'));

        AccessoryCalculation::updateCalculations($accessory);

        request()->session()->flash('message', 'Accessories consumed successfully.');

        $messageView = view('partials.success')->render();

        return response()->json(['status' => true, 'message' => $messageView]);
    }


    /**
     * Delete the specified accessory.
     *
     * @param int $id The ID of the accessory to be deleted.
     *
     * @return \Illuminate\Http\RedirectResponse
     */
    public function destroy($id)
    {
        $accessory = Accessory::findOrFail($id);
        $accessory->delete();

        return redirect()->route('accessories.index')->with('message', __('message.accessory_deleted'));
    }


    /**
     * Update an accessory based on the specified action.
     *
     * @param mixed $accessory The accessory to be updated.
     *
     * @return mixed The result of the action method, or false if the action is not found.
     */
    public function update($accessory)
    {
        if (request()->has('action') && method_exists($this, request('action'))) {
            $action = request('action');

            return $this->{$action}($accessory);
        }

        return false;
    }


    /**
     * Populates the filter fields for accessories.
     *
     * @return \Illuminate\Http\JsonResponse|null
     */
    public function getSearchAccessories()
    {
        $search = request('search');

        if (!empty($search)) {
            $result = SearchAccessories::getSearchFields($search);

            if (!$result->count()) {
                $result[] = ['value' => '0', 'label' => __('message.no_result'), 'data' => ''];
            }

            return response()->json($result);
        }

        return null;
    }


    /**
     * Filter accessories based on the provided criteria.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function filter()
    {
        $purchaseFile = '';
        $makeAndModel = request('make_and_model');
        $location = request('location');
        $purchaseIndicator = request('purchase_indicator');

        $accessories = Accessory::with(['location', 'makeAndModel', 'technicalSpec'])->orderBy('id', 'desc');

        if ($makeAndModel) {
            $accessories = $accessories->whereIn('make_and_model_id', $makeAndModel);
        }

        if ($location) {
            $accessories = $accessories->whereIn('location_id', $location);
        }

        if ($purchaseIndicator) {
            $accessories = $accessories->whereIn('purchase_indicator', $purchaseIndicator);
        }

        $accessories = $accessories->get();

        if ($purchaseIndicator) {
            $purchaseFile = PurchaseFileGenerator::create($accessories);
        }

        $assetView = view('accessories.partials.accessories', compact('accessories', 'purchaseIndicator', 'purchaseFile'))->render();

        return response()->json($assetView);
    }


    /**
     * List technical specifications for a given make and model.
     *
     * @return \Illuminate\Support\Collection
     */
    public function listTechnicalSpec()
    {
        $technicalSpecs = TechnicalSpecs::accessories()->active()->where('make_and_model_id', request('id'))->orderBy('details')->get();

        return $technicalSpecs->map(
            function ($item) {
                return [
                    'id' => $item->id,
                    'details' => $item->details
                ];
            }
        );
    }


    /**
     * Retrieve saved search parameters for accessories.
     *
     * @return mixed The saved search parameters.
     */
    public function getSavedSearchParameters()
    {
        return AccessoryService::getSavedSearchParameters();
    }


    /**
     * Gets the name for save the search
     *
     * @return string
     */
    public function getSavedSearchReportName()
    {
        return 'accessory';
    }
}
