<?php

namespace App\Http\Controllers\Asn\Presidio;

use Exception;
use App\Models\Asset;
use App\Models\AssetType;
use App\Models\AssetStatus;
use App\Models\AssetHistory;
use Illuminate\Http\Request;
use App\Models\AssetTracking;
use App\Models\TechnicalSpecs;
use App\Models\AsnHardwareMapping;
use App\Http\Controllers\Controller;
use App\Http\Traits\Asn\AsnAssetTrait;
use Illuminate\Support\Facades\Artisan;
use App\Repositories\HardwareStandards;
use App\Http\Responses\ReportJsonResponse;
use App\Services\Asn\Presidio\PresidioAssetsService;
use App\Services\Asn\Presidio\PresidioHardwareMappingService;

class PresidioAssetsController extends Controller
{
    use AsnAssetTrait;

    /**
     * Constructor for ReceivePresidioAssetsController
     *
     * @param PresidioAssetsService $PresidioAssetsService
     */
    public function __construct(protected PresidioAssetsService $service, protected PresidioHardwareMappingService $mappingService)
    {
        $this->service          = $service;
        $this->mappingService   = $mappingService;
    }

    /**
     * Show received assets
     *
     * @return view
     */
    public function index()
    {
        $hardwareStandards  = app(HardwareStandards::class)->getHardwareStandardsWithoutAccessories();
        $assetTypes         = AssetType::allWithoutComputerAccessories()->pluck('name', 'id');
        $techSpecs          = TechnicalSpecs::pluck('details', 'id');
        $deliveryStatuses   = AssetTracking::orderBy('shipment_status')->whereNotNull("shipment_status")->distinct("shipment_status")->pluck('shipment_status');

        return view('asn.presidio.presidio-assets', compact('assetTypes', 'hardwareStandards', 'techSpecs', 'deliveryStatuses'));
    }

    /**
     * Retrieve filtered data for Presidio assets in transit.
     *
     * @param \Illuminate\Http\Request $request The incoming HTTP request containing DataTable parameters.
     *
     * @return \App\Http\Responses\ReportJsonResponse The JSON response containing the filtered asset data.
     */
    public function data(Request $request)
    {
        $filteredData  = $this->service->data('presidio_in_transit');
        $assets        = $filteredData['assets'];

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

        if (!empty($assets)) {

            $data = $this->service->getAsnAssetData($assets, $start, $data);
        }

        return new ReportJsonResponse($request->input('draw'), $data, $filteredData['count']);
    }

    /**
     * Export Presidio assets in transit to a CSV file.
     *
     * This method retrieves filtered data for assets in transit, processes it,
     * and exports it to a CSV format. It sets the maximum execution time and
     * memory limit to accommodate large datasets.
     *
     * @return \Symfony\Component\HttpFoundation\StreamedResponse The CSV file response.
     */
    public function export()
    {
        setUnlimitedExecutionTimeAndMemoryLimit();

        $data   = [];
        $datas  = collect();
        $filteredData   = $this->service->getExportData('presidio_in_transit')->get() ?? [];
        $datas->push($this->service->getAsnAssetExportData($filteredData, 0, $data, 'Presidio'));

        return exportToCsv($datas->toArray());
    }

    /**
     * Update an existing Presidio asset.
     *
     * @return \Illuminate\Http\JsonResponse The JSON response with validation errors or a success message.
     */
    public function updateAsset()
    {
        $error = $this->service->validateAsset();

        if ($error) {
            return response()->json($error);
        }

        $asset = Asset::findOrFail(request('id'));

        if ($asset) {

            $data = $this->getDataToUpdate();
            $asset->update($data);
        }

        return ['status' => "success"];
    }

    /**
     * Store a new Presidio asset hardware mapping.
     *
     * Validate the incoming request data and store a new hardware mapping
     * for a Presidio asset. If a mapping for the same part # already exists,
     * return an error response.
     *
     * @return \Illuminate\Http\JsonResponse The JSON response containing any
     *         validation errors or a success message.
     */
    public function addAssetHardwareMapping()
    {
        $error = $this->mappingService->validateHardwareMapping();

        if ($error) {

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

        $presidioHardwareMapping = AsnHardwareMapping::where('provider', 'presidio')->where('part_no', request('part_no'))->exists();

        if ($presidioHardwareMapping) {

            return response()->json(['Mapping for this part # already exist']);
        }

        $this->mappingService->store();

        return ['status' => "success"];
    }

    /**
     * Delete specific asset
     *
     * @param int $id
     *
     * @return string
     */
    public function deleteAsset($id)
    {
        try {

            $presidioInTransitId = AssetStatus::where('slug', 'presidio_in_transit')->value('id');
            $asset = Asset::where('asset_status_id', $presidioInTransitId)->find($id);

            if ($asset) {

                $asset->delete();
                AssetHistory::where('asset_id', $id)->delete();
            }

            return response()->json('success');
        } catch (Exception $e) {

            return response()->json('error');
        }
    }

    /**
     * Mark multiple Presidio assets as received.
     *
     * @return \Illuminate\Http\JsonResponse The JSON response containing any
     *         validation errors or a success message.
     */
    public function markReceived()
    {
        if (!request('location')) {
            return response()->json('Location field is empty!');
        }

        // if (!request('ticket')) {
        //     return response()->json('Ticket field is empty!');
        // }

        $received = request('received');
        $notMappedAssets = $this->receiveAsnAsset($received, 'presidio_in_transit');

        if (count($notMappedAssets) > 0) {
            return response()->json(implode(', ', $notMappedAssets) . " - These Asset(s) were not mapped, Please Map them before receiving.\n");
        }

        return response()->json('Success');
    }

    /**
     * Mark a single Presidio asset as received.
     *
     * @return \Illuminate\Http\JsonResponse The JSON response containing any
     *         validation errors or a success message.
     */
    public function markSingleItemReceived()
    {
        $inputData['asset_id']      = request('asset_id');
        $inputData['location_id']   = request('location_id');
        $inputData['user_id']       = request('user_id');
        $inputData['status_class']  = 'PresidioAssetReceived';

        if ((!empty($inputData['location_id'])) && (!empty($inputData['user_id']))) {

            return response()->json(['status' => "Error", 'message' => "Need to select a valid Location or User"]);
        }

        $asset = Asset::findOrFail($inputData['asset_id']);

        if (!$this->validateAssetFields($asset)) {

            return response()->json(['status' => "Error", 'message' => $asset->serial_no . " - This Asset was not mapped. Please map before receiving."]);
        }

        $message = $this->updateSingleAsset($asset, $inputData);

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

    /**
     * Manually sync Presidio assets.
     *
     * @return \Illuminate\Http\JsonResponse The JSON response containing the
     *         output of the Artisan command.
     */
    public function manualSync()
    {
        setUnlimitedExecutionTimeAndMemoryLimit();

        Artisan::call('presidio:import');
        $message = Artisan::output();

        if (str_contains($message, "Assets imported")) {

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

        return response()->json(['status' => 'error', 'message' => "Some error occurred! Please try later."]);
    }

    /**
     * Update PO details of assets
     */
    // public function bulkUpdatePo()
    // {
    //     $received = request('received');
    //     $response = $this->bulkUpdateAssetPoDetails($received);

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