<?php

namespace App\Services\Reports\BrandNewExtExport;

use App\Http\Responses\ReportOutputData;
use App\Repositories\CommonFilterRepository;
use App\Repositories\Reports\BrandNewAssignedToContractorRepository;
use App\Services\ReportGraphAndDataExport;
use App\Services\Reports\AbstractReportService;
use Carbon\Carbon;
use Illuminate\Support\Facades\Storage;
use Maatwebsite\Excel\Facades\Excel;

class BrandNewAssignedToContractorService extends AbstractReportService
{
    protected $repository;
    protected $commonFilterRepository;
    protected $reportOutputData;

    /**
     * Constructor for the BrandNewAssignedToContractorService
     *
     * @param BrandNewAssignedToContractorRepository $repository
     * @param CommonFilterRepository $commonFilterRepository
     * @param ReportOutputData $reportOutputData
     */
    public function __construct(BrandNewAssignedToContractorRepository $repository, CommonFilterRepository $commonFilterRepository, ReportOutputData $reportOutputData)
    {
        $this->repository = $repository;
        $this->commonFilterRepository = $commonFilterRepository;
        $this->reportOutputData = $reportOutputData;
    }

    /**
     * Get Assets Changed from BrandNew to Assigned status for the employees between date range
     *
     * @return array
     */
    public function getGraphData()
    {
        $inputData = $this->getInputData();
        $assetHistoryQueryResponse = $this->getAssetsChangedFromBrandNewAssignedToContractor($inputData);
        $assetHistory = $assetHistoryQueryResponse->orderBy('id', 'desc')->get();
        $dateFrom = $this->getFromDate($inputData);
        $dateTo = $this->getToDate($inputData);
        $months = get_month_year_names_with_all_months($dateFrom);
        $assetsGroupedByMonths = $this->groupByMonths($assetHistory);
        $datas = $this->getFormattedData($assetsGroupedByMonths, $months);
        $monthNames = collect($months)->flatten()->toArray();

        return ['datas' => $datas, 'months' => $monthNames];
    }

    /**
     * Fetching the query and add the filters with the query
     *
     * @param array $inputData
     * @return object $asset
     */
    public function getAssetsChangedFromBrandNewAssignedToContractor($inputData)
    {
        $dateFrom = $this->getFromDate($inputData);
        $dateTo = $this->getToDate($inputData);
        $assetHistoryQuery = $this->repository->getAssetsChangedFromBrandNewAssignedToContractor($dateFrom, $dateTo);
        $assetHistoryQuery = $this->filterWithInputData($assetHistoryQuery, $inputData);

        return $assetHistoryQuery;
    }

    /**
     * Get the start of year
     * @param array $inputData
     * @return Carbon
     */
    public function getFromDate($inputData)
    {
        $date = $inputData['date'] ?? '';

        if (!empty($date)) {
            $dateFrom = Carbon::createFromFormat("M Y", $date)->startOfMonth();

            return $dateFrom;
        } 
        
        $year = $inputData['year'] ?? '';

        if (!empty($year)) {
            $dateFrom = format_date_from_year_to_carbon($year)->startOfYear();
        } else {
            $now = Carbon::now();
            $dateFrom = $now->copy()->startOfYear();
        }

        return $dateFrom;
    }

    /**
     * Get the end of year
     * @param array $inputData
     * @return Carbon
     */
    public function getToDate($inputData)
    {
        $date = $inputData['date'] ?? '';

        if (!empty($date)) {
            $dateFrom = Carbon::createFromFormat("M Y", $date)->endOfMonth();

            return $dateFrom;
        } 
        
        $year = $inputData['year'] ?? '';

        if (!empty($year)) {
            if ($year == Carbon::now()->year) {
                $dateTo = Carbon::now();
            } else {
                $dateTo = format_date_from_year_to_carbon($year)->endOfYear();
            }
        } else {
            $dateTo = Carbon::now();
        }

        return $dateTo;
    }


    /**
     * Group Assets by months
     * @param \Illuminate\Database\Eloquent\Collection $assets
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public function groupByMonths($assets)
    {
        return $assets->groupBy(function ($assets) {
            return intval(Carbon::parse($assets->getRawOriginal('created_at'))->month);
        });
    }

    /**
     * Format the collection for graph
     * @param \Illuminate\Database\Eloquent\Collection $assets
     * @param array $interval may be weeks or months
     * @return array
     */
    public function getFormattedData($assets, array $interval)
    {
        $formattedData = [];

        foreach ($interval as $intervalNumber => $intervalName) {
            $intervalNumber = explode('-', $intervalNumber)[0];
            $intervalCount = $assets->get($intervalNumber);
            $formattedData[] = is_countable($intervalCount) ? count($intervalCount) : 0;
        }
        return $formattedData;
    }
 
    /**
     * Retrieves the data for the report
     *
     * @return array The data array containing the following keys:
     *               - result: The result of the query
     *               - count: The count of the result
     */
    public function data()
    {
        $inputData  = $this->getInputData();
        $result     = $this->getAssetsChangedFromBrandNewAssignedToContractor($inputData);
        $count      = $result->count();
        $result     = $this->reportOutputData->getOutputData($result, ['id' => 'desc']);

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

    /**
     * Filter the query results based on the filters applied
     *
     * @param  mixed $assets
     * @param  mixed $inputData
     * @return object
     */
    public function filterWithInputData($query, $inputData)
    {
        $query = $this->commonFilterRepository->filterWithNestedWhereHasRelationFields($query, 'asset', 'assetType', 'id', $inputData['asset_type_id']);
        $query = $this->commonFilterRepository->filterWithWhereHasRelationFields($query, 'asset', 'make_and_model_id', $inputData['make_and_model_id']);
        $query = $this->commonFilterRepository->filterWithWhereHasRelationFields($query, 'asset', 'technical_spec_id', $inputData['technical_spec_id']);
        $query = $this->commonFilterRepository->filterWithDirectFields($query, 'old_location_id', $inputData['old_location_id']);

        return $query;
    }

    /**
     * Retrieves the input data from the requested data array.
     *
     * @return array The input data array containing the following keys:
     */
    public function getInputData()
    {
        $requestedData = request()->form ?? request()->all();
        $inputData =  [
            'asset_type_id' => $requestedData['asset_type_id'] ?? '',
            'make_and_model_id'    => $requestedData['make_and_model_id'] ?? '',
            'technical_spec_id' => $requestedData['technical_spec'] ?? '',
            'old_location_id' => $requestedData['old_location_id'] ?? '',
            'year'    => $requestedData['year'] ?? '',
            'date'    => $requestedData['date'] ?? '',
        ];

        return $inputData;
    }

    /**
     * Retrieves the nested data for generating a report.
     *
     * @param $asset The asset object for which the report is being generated.
     */
    public function getReportNestedData($assetHistory)
    {
        $nestedData = [];
        $nestedData['report']['asset_tag']          = $nestedData['export']['Asset Tag #'] = $assetHistory->asset->asset_tag;
        $nestedData['report']['serial_no']          = $nestedData['export']['Serial #'] = $assetHistory->asset->serial_no;
        $nestedData['report']['asset_type']         = $nestedData['export']['Asset Type'] = $assetHistory->asset->assetType?->name;
        $nestedData['report']['hardware_standard']  = $nestedData['export']['Hardware Standard'] = $assetHistory->asset->makeAndModel?->makeModelName;
        $nestedData['report']['tech_specs']         = $nestedData['export']['Technical Specs'] = $assetHistory->asset->technicalSpec?->details;
        $nestedData['report']['user']               = $nestedData['export']['User'] = disableCSVInjection($assetHistory->newUser?->user_name);
        $nestedData['report']['title']              = $nestedData['export']['Title'] = disableCSVInjection($assetHistory->newUser?->position?->name);
        $nestedData['report']['from_location']      = $nestedData['export']['From Location'] = disableCSVInjection($assetHistory->oldLocation?->room_name);
        $nestedData['report']['assigned_date'] = $nestedData['export']['Assigned Date'] = disableCSVInjection($assetHistory->created_date);
        $nestedData['report']['age']                = $nestedData['export']['Asset Age'] = $assetHistory->asset->AssetAge;

        return $nestedData;
    }

    /**
     * Retrieves the nested data for a given item.
     *
     * @param mixed $item The item for which to retrieve the nested data.
     * @param int $index The index of the item.
     * @return array The nested data for the item.
     */
    public function getNestedData($item, $index)
    {
        $commonData = $this->getReportNestedData($item);
        $nestedData = $commonData['report'];
        $nestedData['serial_no'] = generateAssetLink($item->asset?->id, $nestedData['serial_no']);
        $nestedData['asset_tag'] = generateAssetLink($item->asset?->id, $nestedData['asset_tag']);
        $nestedData['id'] = $index;
        $nestedData['user'] = $item->newUser ? generateUserLink($item->newUser->id, $item->newUser->user_name) : '';
        $nestedData['from_location'] = !empty($nestedData['from_location']) ? generateLocationLink($item->old_location_id, $nestedData['from_location']) : '';

        return $nestedData;
    }

    /**
     * Create output data
     * @params $assets,$start, $data, $type
     * @return array
     * */
    public function getOutputData($assets, $data)
    {
        $parentIndex = 0;

        foreach ($assets as $asset) {
            ++$parentIndex;
            $nestedData = $this->getNestedData($asset, $parentIndex);
            $data[] = $nestedData;
        }

        return $data;
    }

    /**
     * Export the data.
     *
     * @return mixed
     */
    public function exportData()
    {
        $inputData = $this->getInputData();
        $assets = $this->getAssetsChangedFromBrandNewAssignedToContractor($inputData);
        $assets = $assets->orderBy('id', 'desc');

        return $assets;
    }

    /**
	 * Export the graph and data list to Excel.
	 *
	 * @param array $dataCollection The collection of data.
	 */
	public function exportGraphAndDataListToExcel(array $dataCollection)
	{
		$name = str_replace(' ', '_', request('name'));
		$name = str_replace('/', '_', $name);
		$sheetTitle = str_replace(' ', '_', request('sheettitle'));
		$sheetTitle = str_replace('/', '_', $sheetTitle);

		$headings = array_keys($dataCollection[0][0]);
		$image = storage_path('app/' . request('image'));
		$filename = request("filename") . time() . '.xlsx';
		$excel = Excel::download(new ReportGraphAndDataExport($dataCollection, $image, $headings, $sheetTitle), $filename, \Maatwebsite\Excel\Excel::XLSX);
		$img = explode('public/', request('image'));
		Storage::disk('public')->delete($img[1]);

		return $excel;
	}
}
