<?php

namespace App\Services\NewHire;

use App\Http\Responses\ReportOutputData;
use App\Repositories\CommonFilterRepository;
use App\Repositories\NewHire\AverageDeploymentTimeRepository;
use App\Services\Reports\AbstractReportService;

class AverageDeploymentTimeService extends AbstractReportService
{

    /**
     * Constructor for the class.
     *
     * @param AverageDeploymentTimeRepository $repository The repository for average deployment time.
     * @param CommonFilterRepository $commonFilterRepository The repository for common filters.
     * @param ReportOutputData $reportOutputData The report output data.
     */
    public function __construct(protected AverageDeploymentTimeRepository $repository, protected CommonFilterRepository $commonFilterRepository, protected ReportOutputData $reportOutputData) {}

    /**
     * Data to the Report data table
     *
     * @return void
     */
    public function data()
    {
        $inputData  = $this->getInputData();
        $result     = $this->repository->getData();
        $result     = $this->filterWithInputData($result, $inputData);
        $count      = $result->count();
        $result     = $this->reportOutputData->getOutputData($result, ['users.hire_date' => 'desc']);

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

    /**
     * Filter the query results based on the filters applied
     *
     * @param  mixed $query
     * @param  array $inputData
     * @return object
     */
    public function filterWithInputData($query, $inputData)
    {
        $query = $this->commonFilterRepository->filterWithRelationalDateRange($query, 'user', 'hire_date', $inputData['start_date'], $inputData['end_date']);
        $query = $this->commonFilterRepository->filterWithDirectDateRange($query, 'asset_histories.updated_at', $inputData['assigned_date_from'], $inputData['assigned_date_to']);
        $query = $this->commonFilterRepository->filterWithWhereHasRelationFields($query, 'user', 'department_id', $inputData['department_id']);
        $query = $this->commonFilterRepository->filterWithWhereHasRelationFields($query, 'user', 'employee_type_id', $inputData['employee_type_id']);
        $query = $this->commonFilterRepository->filterWithWhereHasRelationFields($query, 'user', 'city', $inputData['work_location']);
        $query = $this->commonFilterRepository->searchToMultipleFieldsWithRelation($query, 'user', ['first_name', 'last_name', 'employee_id', 'email'], $inputData['employee_name']);
        $query = $this->commonFilterRepository->filterWithDirectFields($query, 'asset_tag', $inputData['asset_tag']);
        $query = $this->commonFilterRepository->filterWithDirectFields($query, 'serial_no', $inputData['serial_no']);

        return $query;
    }

    /**
     * get input data
     *
     * @return array
     * */
    public function getInputData()
    {
        $requestedData = request()->form ?? request()->all();

        return [
            'department_id' => $requestedData['department'] ?? '',
            'start_date' => $requestedData['date_from'] ?? '',
            'end_date' => $requestedData['date_to'] ?? '',
            'work_location' => $requestedData['work_location'] ?? '',
            'employee_type_id' => $requestedData['employee_type'] ?? '',
            'employee_name' => $requestedData['employee_name'] ?? '',
            'assigned_date_from' => $requestedData['assigned_date_from'] ?? '',
            'assigned_date_to' => $requestedData['assigned_date_to'] ?? '',
            'asset_tag' => $requestedData['asset_tag'] ?? '',
            'serial_no' => $requestedData['serial_no'] ?? '',
        ];
    }

    /**
     * get Report and export common Nested Data
     *
     * @param  mixed $asset
     * @return void
     */
    public function getReportNestedData($asset)
    {
        $assignedHistory = $this->repository->getAssignedAssetsHistory($asset->id, $asset->user?->id);
        $nestedData = [];
        $nestedData['report']['asset_tag']      = $nestedData['export']['Asset Tag #'] = $asset->asset_tag;
        $nestedData['report']['serial_no']      = $nestedData['export']['Serial #'] = $asset->serial_no;
        $nestedData['report']['user_name']      = $nestedData['export']['User Name'] = $asset->user?->first_name . ' ' . $asset->user?->last_name;
        $nestedData['report']['hire_date']      = $nestedData['export']['Hire Date'] = $asset->user?->hire_date;
        $nestedData['report']['assigned_date']  = $nestedData['export']['Assigned Date'] = $assignedHistory->updated_at;
        $nestedData['report']['no_of_days']     = $nestedData['export']['# of Days'] = getDaysDifference(convert_to_db_datetime($assignedHistory->updated_at), convert_to_db_datetime($asset->user?->hire_date));
        $nestedData['report']['title']          = $nestedData['export']['Title'] = $asset->user?->business_title;
        $nestedData['report']['department']     = $nestedData['export']['Department'] = $asset->user?->department?->name;
        $nestedData['report']['work_location']  = $nestedData['export']['Work Location'] = $asset->user?->city;
        $nestedData['report']['storage_room']   = $nestedData['export']['Storage Room'] = $asset->location?->room_name;
        $nestedData['report']['ticket_no']      = $nestedData['export']['Ticket #'] = $asset->ticket_no;
        $nestedData['report']['modified_date']  = $nestedData['export']['Modified Date'] = $assignedHistory->updated_at;
        $nestedData['report']['modified_by']    = $nestedData['export']['Modified By'] = $assignedHistory->user?->first_name . ' ' . $assignedHistory->user?->last_name;

        return $nestedData;
    }

    /**
     * Retrieves nested data for the given item and index.
     *
     * @param mixed $item
     * @param int $index
     * @return array
     */
    public function getNestedData($item, $index)
    {
        $nestedData = [];
        $commonData = $this->getReportNestedData($item);
        $nestedData = $commonData['report'];
        $assignedHistory = $this->repository->getAssignedAssetsHistory($item->id, $item->user?->id);
        $nestedData['serial_no'] = generateAssetLink($item->id, $item->serial_no);
        $nestedData['asset_tag'] = generateAssetLink($item->id, $item->asset_tag);
        $nestedData['user_name'] = generateUserLink($item->user?->id, $item->user?->user_name);
        $nestedData['modified_by'] = generateUserLink($assignedHistory->user?->id, $assignedHistory->user?->user_name);
        $nestedData['id'] = $index;

        return $nestedData;
    }

    /**
     * Exports the data.
     *
     * @return mixed
     */
    public function exportData()
    {
        $inputData = $this->getInputData();
        $assets = $this->repository->getData();
        $assets = $this->filterWithInputData($assets, $inputData);
        $assets = $assets->orderBy('users.hire_date', 'DESC');

        return $assets;
    }

    /**
     * Retrieves the widget data.
     *
     * @return array The widget data containing the new hires count, assets assigned on date count, and assets assigned after start date count.
     */
    public function getWidgetData(): array
    {
        $inputData = $this->getInputData();
        $newHiresCount = $this->newHiresCount($inputData);
        $assetsAssignedCount = $this->assetsAssignedCount($inputData);
        $assetsAssignedOnDate = $this->assetsAssignedOnDateCount($inputData);
        $assetsAssignedAfterStartDateCount = $this->assetsAssignedAfterStartDateCount($inputData);

        return compact('newHiresCount', 'assetsAssignedOnDate', 'assetsAssignedAfterStartDateCount', 'assetsAssignedCount');
    }

    /**
     * Calculate the count of assigned assets based on the input data.
     *
     * @param array $inputData The input data for filtering assets.
     * @return int The count of assigned assets.
     */
    public function assetsAssignedCount(array $inputData): int
    {
        $assets = $this->repository->getData();
        $assets = $this->filterWithInputData($assets, $inputData);

        return $assets->count();
    }

    /**
     * Retrieves the count of new hires based on the provided input data.
     *
     * @param array $inputData An array containing the input data for retrieving new hires count.
     * @return int The count of new hires.
     */
    public function newHiresCount(array $inputData): int
    {
        $newHires = $this->repository->newHires();
        $newHires = $this->commonFilterRepository->filterWithDirectDateRange($newHires, 'hire_date', $inputData['start_date'], $inputData['end_date']);
        $newHires = $this->commonFilterRepository->filterWithDirectFields($newHires, 'department_id', $inputData['department_id']);
        $newHires = $this->commonFilterRepository->filterWithDirectFields($newHires, 'employee_type_id', $inputData['employee_type_id']);
        $newHires = $this->commonFilterRepository->filterWithDirectFields($newHires, 'city', $inputData['work_location']);
        $newHires = $this->repository->searchUserWithGeneralQueries($newHires, $inputData['employee_name']);

        return $newHires->count();
    }

    /**
     * Returns the count of assets assigned on a specific date.
     *
     * @param array $inputData The input data containing filters for the assets.
     * @return int The count of assets assigned on the specified date.
     */
    public function assetsAssignedOnDateCount(array $inputData): int
    {
        $assets = $this->repository->getData();
        $assets = $this->repository->assetsAssignedOnDate($assets);
        $assets = $this->filterWithInputData($assets, $inputData);

        return $assets->count();
    }

    /**
     * Calculates the count of assets assigned after a specific start date.
     *
     * @param array $inputData The input data used for filtering the assets.
     * @return int The count of assets assigned after the start date.
     */
    public function assetsAssignedAfterStartDateCount(array $inputData): int
    {
        $assets = $this->repository->getData();
        $assets = $this->repository->assetsAssignedAfterStartDate($assets);
        $assets = $this->filterWithInputData($assets, $inputData);

        return $assets->count();
    }
}
