<?php

namespace App\Services\InventoryConsumption;

use App\Http\Responses\ReportOutputData;
use App\Models\AssetType;
use App\Models\MakeAndModel;
use App\Models\Manufacturer;
use App\Models\TechnicalSpecs;
use App\Repositories\InventoryConsumption\ConsumptionReportRepository;
use App\Services\Reports\AbstractReportService;
use App\Repositories\CommonFilterRepository;

/**
 * Service Class for Consumption Report
 */
class ConsumptionReportService extends AbstractReportService
{
    protected $repository;
    protected $commonFilterRepository;
    protected $reportOutputData;

    /**
     * Constructor for ConsumptionReportService.
     *
     * @param ConsumptionReportRepository $consumptionReportRepository The repository instance
     * @param CommonFilterRepository $commonFilterRepository The repository for the CommonFilter entity.
     * @param ReportOutputData $reportOutputData The ReportOutputData entity
     */
    public function __construct(ConsumptionReportRepository $consumptionReportRepository, CommonFilterRepository $commonFilterRepository, ReportOutputData $reportOutputData)
    {
        $this->repository = $consumptionReportRepository;
        $this->commonFilterRepository = $commonFilterRepository;
        $this->reportOutputData = $reportOutputData;
    }

    /**
     * Retrieves the filter data options for various fields.
     *
     * @return array The filter data options for asset types, manufacturers, hardware standards, technical specifications.
     */
    public function getFilterDataOptions()
    {
        $includeAssetTypeIds = AssetType::getBySlugList(config('reports.consumption_report.include_asset_types'))->pluck('id')->toArray();
        $filterDataOptions['assetTypes'] = AssetType::whereIn('id', $includeAssetTypeIds)->orderBy('name')->get();
        $filterDataOptions['manufacturers'] = Manufacturer::whereHas('makeAndModel', function ($query) use($includeAssetTypeIds) {
            $query->whereIn('asset_type_id', $includeAssetTypeIds);
        })->orderBy('name')->get();
        $filterDataOptions['makeandmodel'] = MakeAndModel::GetWithAssetTypeIds($includeAssetTypeIds)->valid()->orderBy('name', 'ASC')->get();
        $filterDataOptions['techSpecs'] =  TechnicalSpecs::whereIn('make_and_model_id', MakeAndModel::GetWithAssetTypeIds($includeAssetTypeIds)->pluck('id')->toArray())->whereNotNull('details')->orderBy('details')->get();

        return $filterDataOptions;
    }

    /**
     * Get the data for the Consumption Report.
     *
     * @return array
     */
    public function data()
    {
        $requestData = request()->form;
        $inputData = $this->getInputData($requestData);
        $assets = $this->repository->getConsumptionData();
        $assets = $this->filter($assets, $inputData);
        $count = $assets->count();
        $assets = $this->reportOutputData->getOutputData($assets, ['asset_histories.created_at' => 'desc']);

        return compact('assets', 'count');
    }

    /**
     * Get the input data for filtering.
     *
     * @return array
     */
    public function getInputData($requestData)
    {
        return [
            'assetTag' => $requestData['asset_tag'] ?? '',
            'serial' => $requestData['serial_no'] ?? '',
            'assetStatus' => $requestData['asset_status'] ?? '',
            'hardwareStandard' => $requestData['make_and_model'] ?? '',
            'manufacturer' => $requestData['manufacturer'] ?? '',
            'assetType' => $requestData['asset_type'] ?? '',
            'technicalSpec' => $requestData['technical_spec'] ?? '',
            'newLocation' => $requestData['new_location'] ?? '',
            'user' => $requestData['user'] ?? '',
            'user_type' => $requestData['employee_type'] ?? '',
            'workLocation' => $requestData['work_location'] ?? '',
            'department' => $requestData['department'] ?? '',
            'location' => $requestData['location'] ?? '',
            'country' => $requestData['country'] ?? '',
            'region' => $requestData['region'] ?? '',
            'dateAssignedFrom' => $requestData['assigned_date_from'] ?? '',
            'dateAssignedTo' => $requestData['assigned_date_to'] ?? '',
            'previous_status' => $requestData['previous_status'] ?? '',
            'createdDateFrom' => $requestData['created_date_from'] ?? '',
            'createdDateTo' => $requestData['created_date_to'] ?? '',
            'lastModifiedBy' => $requestData['modified_by'] ?? '',
        ];
    }

    /**
     * Report nested data for both export and report.
     *
     * @param mixed $history The history object.
     * @return array The nested data for the report.
     */
    public function getReportNestedData($history)
    {

        $nestedData['report']['asset_tag'] = $nestedData['export']['Asset Tag #'] = disableCSVInjection(optional($history->asset)->asset_tag ?? '');
        $nestedData['report']['serial'] = $nestedData['export']['Serial #'] = disableCSVInjection(optional($history->asset)->serial_no ?? '');
        $nestedData['report']['asset_type'] = $nestedData['export']['Asset Type'] = disableCSVInjection(optional(optional($history->asset)->assetType)->name ?? '');
        $nestedData['report']['manufacturer'] = $nestedData['export']['Manufacturer'] = disableCSVInjection(optional(optional(optional($history->asset)->makeAndModel)->manufacturer)->name ?? '');
        $nestedData['report']['hardware_standard'] = $nestedData['export']['Hardware Standard'] = disableCSVInjection(optional(optional($history->asset)->makeAndModel)->name ?? '');
        $nestedData['report']['tech_specs'] = $nestedData['export']['Technical Specs'] = disableCSVInjection(optional(optional($history->asset)->technicalSpec)->details ?? '');
        $nestedData['report']['asset_status'] = $nestedData['export']['Asset Status'] = disableCSVInjection(optional(optional($history->asset)->assetStatus)->name ?? '');
        $nestedData['report']['prev_asset_status'] = $nestedData['export']['Previous Asset Status'] = disableCSVInjection($history->oldStatus?->name);
        $nestedData['report']['userlocation'] = $nestedData['export']['User/Location'] = disableCSVInjection($history->newUser ? (optional($history->newUser)->email) : ($history->newLocation ? (optional($history->newLocation)->room_name) : ''));
        $nestedData['report']['user_type'] = $nestedData['export']['User Type'] = disableCSVInjection(optional(optional($history->newUser)->employeeType)->name ?? '');
        $nestedData['report']['storage_location'] = $nestedData['export']['Storage Location'] = disableCSVInjection(optional($history->oldLocation)->room_name ?? '');
        $nestedData['report']['storage_country'] = $nestedData['export']['Storage Country'] = disableCSVInjection(optional(optional(optional($history->oldLocation)->siteCode)->country)->name ?? '');
        $nestedData['report']['storage_region'] = $nestedData['export']['Storage Region'] = disableCSVInjection(optional(optional(optional($history->oldLocation)->siteCode)->region)->name ?? '');
        $nestedData['report']['user_department'] = $nestedData['export']['User Department'] = disableCSVInjection(optional(optional($history->newUser)->department)->name ?? '');
        $nestedData['report']['user_work_location'] = $nestedData['export']['User Work Location'] = disableCSVInjection(optional($history->newUser)->city ?? '');
        $nestedData['report']['date_assigned'] = $nestedData['export']['Date Assigned'] = disableCSVInjection(parse_gerneral_datetime($history->getRawOriginal('created_at')));
        $nestedData['report']['serial_no'] = $nestedData['export']['Ticket #'] = disableCSVInjection($history->ticket_no);
        $nestedData['report']['created_date'] = $nestedData['export']['Created Date'] = disableCSVInjection(optional($history->asset)->created_at ?? '');
        $nestedData['report']['last_modified_by'] = $nestedData['export']['Last Modified By'] = disableCSVInjection(optional(optional(optional($history->asset)->latestAssetHistory)->user)->email ?? '');

        return $nestedData;
    }

    /**
     * Retrieves the nested data for a given history and index.
     *
     * @param mixed $history The history object.
     * @param mixed $index The index of the nested data.
     * @return array The nested data.
     */
    public function getNestedData($history, $index)
    {
        $nestedData = [];
        $commonData = $this->getReportNestedData($history);
        $nestedData = $commonData['report'];

        $userLocationLink = $history->newUser ? generateUserLink($history->newUser?->id, $nestedData['userlocation']) : generateLocationLink($history->newLocation?->id, $nestedData['userlocation']);

        $nestedData['id']   = $index;
        $nestedData['asset_tag'] = generateAssetLink($history->asset->id, $nestedData['asset_tag']);
        $nestedData['serial'] = generateAssetLink($history->asset?->id, $nestedData['serial']);
        $nestedData['userlocation'] = $userLocationLink;
        $nestedData['storage_location'] = generateLocationLink($history->oldLocation?->id, $nestedData['storage_location']);
        $nestedData['last_modified_by'] = generateUserLink($history->asset?->latestAssetHistory?->user?->id, $nestedData['last_modified_by']);

        return $nestedData;
    }

    /**
     * Filters the given assets based on the input data.
     *
     * @param object $assets The assets to be filtered.
     * @param array $inputData The input data to filter the assets.
     * @return object The filtered assets.
     */
    public function filter($assets, $inputData)
    {
        $assets = $this->commonFilterRepository->filterWithDirectFields($assets, 'new_user_id', $inputData['user']);
        $assets = $this->commonFilterRepository->filterWithDirectFields($assets, 'new_location_id', $inputData['newLocation']);
        $assets = $this->commonFilterRepository->filterWithDirectFields($assets, 'old_location_id', $inputData['location']);
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'newUser', 'city', $inputData['workLocation']);
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'newUser', 'department_id', $inputData['department']);
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'newUser', 'employee_type_id', $inputData['user_type']);
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'oldLocation.siteCode', 'country_id', $inputData['country']);
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'oldLocation.siteCode', 'region_id', $inputData['region']);
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'asset', 'asset_tag', $inputData['assetTag']);
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'asset', 'serial_no', $inputData['serial']);
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'asset', 'asset_status_id', $inputData['assetStatus']);
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'asset', 'asset_type_id', $inputData['assetType']);
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'asset', 'make_and_model_id', $inputData['hardwareStandard']);
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'asset', 'technical_spec_id', $inputData['technicalSpec']);
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'asset.makeAndModel', 'manufacturer_id', $inputData['manufacturer']);
        $assets = $this->commonFilterRepository->filterWithDirectFields($assets, 'old_asset_status_id', $inputData['previous_status']);
        $assets = $this->commonFilterRepository->filterWithDirectDateRange($assets, 'asset_histories.created_at', $inputData['dateAssignedFrom'], $inputData['dateAssignedTo']);
        $assets = $this->commonFilterRepository->filterWithRelationalDateRange($assets, 'asset', 'created_at', $inputData['createdDateFrom'], $inputData['createdDateTo']);
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'asset.latestAssetHistory', 'user_id', $inputData['lastModifiedBy']);

        return $assets;
    }

    /**
     * Export the data.
     *
     * @return mixed
     */
    public function exportData()
    {
        $requestData = request()->all();
        $inputData = $this->getInputData($requestData);
        $assets = $this->repository->getConsumptionData();
        $assets = $this->filter($assets, $inputData);
        $assets = $assets->orderBy('asset_histories.created_at', 'desc');

        return $assets;
    }
}
