<?php

namespace App\Services\Reports;

use App\Models\Location;
use Carbon\Carbon;
use App\Repositories\Reports\BrandNewToAssignedRepository;
use App\Repositories\CommonFilterRepository;
use App\Http\Responses\ReportOutputData;
use Illuminate\Support\Facades\Storage;
use Maatwebsite\Excel\Facades\Excel;
use App\Services\ReportGraphAndDataExport;

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

    /**
     * Constructs a new instance of the class.
     *
     * @param BrandNewToAssignedRepository $repository The repository for the BrandNewToAssigned entity.
     * @param CommonFilterRepository $commonFilterRepository The repository for the CommonFilter entity.
     * @param ReportOutputData $reportOutputData The ReportOutputData entity
     */
    public function __construct(BrandNewToAssignedRepository $repository, CommonFilterRepository $commonFilterRepository, ReportOutputData $reportOutputData)
    {
        $this->repository = $repository;
        $this->commonFilterRepository = $commonFilterRepository;
        $this->reportOutputData = $reportOutputData;
    }

    /**
     * Get data for the graph representation
     *
     * @return array
     */
    public function getGraphData()
    {
        $inputData = $this->getInputData();
        $assetHistoryQueryResponse = $this->getAssetsChangedFromBrandNewToAssigned($inputData);
        $assetHistory = $assetHistoryQueryResponse->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];
    }

    /**
     * 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);
        });
    }

    /**
     * Get Asset data that changed from BrandNew to Assigned status in between the date range
     *
     * @param array $inputData
     * @return object $asset
     */
    public function getAssetsChangedFromBrandNewToAssigned($inputData)
    {
        $dateFrom = $this->getFromDate($inputData);
        $dateTo = $this->getToDate($inputData);
        $assetHistoryQuery = $this->repository->getAssetsChangedFromBrandNewToAssigned($dateFrom, $dateTo);
        $assetHistoryQuery = $this->filterQuery($assetHistoryQuery, $inputData);

        return $assetHistoryQuery;
    }

    /**
     * Filters the query based on the provided assets and input data
     *
     * @param oblect query to be filtered
     * @param array $inputData The input data to filter the query.
     * @return object query after filtering
     */
    public function filterQuery($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->filterWithDirectFields($query, 'old_location_id', $inputData['old_location_id']);
        $query = $this->commonFilterRepository->filterWithWhereHasRelationFields($query, 'asset', 'technical_spec_id', $inputData['technical_spec_id']);

        return $query;
    }

    /**
     * Get the start of date range
     * @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 date range
     * @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;
    }

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

        foreach ($intervals as $intervalNumber => $intervalName) {
            $intervalNumber = explode('-', $intervalNumber)[0];
            $intervalCount = $assets->get($intervalNumber);
            $formattedData[] = is_countable($intervalCount) ? count($intervalCount) : 0;
        }
        return $formattedData;
    }

    /**
     * Get data for the report table.
     *
     * @return array
     */
    public function data()
    {
        $inputData  = $this->getInputData();
        $result     = $this->getAssetsChangedFromBrandNewToAssigned($inputData);
        $count      = $result->count();
        $result     = $this->reportOutputData->getOutputData($result, ['created_at' => 'desc']);

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

    /**
     * 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 nested data for a given asset and index.
     *
     * @param mixed $item The asset object.
     * @param int $index The index of the nested data.
     *
     * @return array The nested data.
     */
    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;
    }

    /**
     * Retrieves the export data based on the input data.
     *
     * @return mixed The export data.
     */
    public function exportData()
    {
        $inputData = $this->getInputData();
        $assets = $this->getAssetsChangedFromBrandNewToAssigned($inputData);
        $assets = $assets->orderBy('created_at', 'desc');

        return $assets;
    }

    /**
     * Returns an array of nested data for exporting.
     *
     * @param mixed $asset The asset object.
     * @return array The nested data array.
     */
    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;
    }

    /**
	 * 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;
	}
}
