<?php

namespace App\Services\Reports;

use App\Repositories\Reports\RepairAndRecoveryRepository;
use App\Repositories\CommonFilterRepository;
use App\Http\Responses\ReportOutputData;
use Carbon\Carbon;

/**
 * Service class for Repair And Recovery Report
 */
class RepairAndRecoveryService extends AbstractReportService
{

    protected $repository;
    protected $commonFilterRepository;
    protected $reportOutputData;

    /**
     * Construct of RepairAndRecoveryService
     * @param RepairAndRecoveryRepository $repository The repository for the Repair And Recovery Repository entity
     * @param CommonFilterRepository $commonFilterRepository The repository for the CommonFilter entity
     * @param ReportOutputData $reportOutputData The ReportOutputData entity
     */
    public function __construct(RepairAndRecoveryRepository $repository, CommonFilterRepository $commonFilterRepository, ReportOutputData $reportOutputData)
    {
        $this->repository = $repository;
        $this->commonFilterRepository = $commonFilterRepository;
        $this->reportOutputData = $reportOutputData;
    }

    /**
     * Data to the Report data table
     *
     * @return void
     */
    public function data()
    {
        $type = request()->form ? request()->form['type'] : request()->all()['type'];
        if ($type == 'pending') {
            $result = $this->repository->getRepairAndRecoveryPendingAssets();
        } else {
            $result = $this->repository->getRepairAndRecoveryAssets();
        }
        $result = $this->filterWithInputData($result);
        $count  = $result->count();
        $result = $this->reportOutputData->getOutputData($result);

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

    /**
     * Filter the query results based on the filters applied
     *
     * @param  mixed $assets
     * @return object
     */
    public function filterWithInputData($assets)
    {
        $requestedData = request()->form ?? request()->all();
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'asset', 'asset_type_id', $requestedData['asset_type'] ?? '');
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'asset', 'make_and_model_id', $requestedData['make_and_model'] ?? '');
        $assets = $this->commonFilterRepository->filterWithDirectFields($assets, 'new_asset_status_id', $requestedData['status'] ?? '');
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'asset', 'location_id', $requestedData['location'] ?? '');
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'asset.makeAndModel.manufacturer', 'id', $requestedData['manufacturer'] ?? '');
        $assets = $this->commonFilterRepository->filterWithDirectDateRange($assets, 'created_at', $requestedData['date_from'], $requestedData['date_to']);

        return $assets;
    }

    /**
     * export Data as csv
     *
     * @return mixed
     */
    public function exportData()
    {
        $type = request()->form ? request()->form['type'] : request()->all()['type'];
        if ($type == 'pending') {
            $assets = $this->repository->getRepairAndRecoveryPendingAssets();
        } else {
            $assets = $this->repository->getRepairAndRecoveryAssets();
        }
        $assets  = $this->filterWithInputData($assets);
        $assets  = $assets->orderBy('id');

        return $assets;
    }


    /**
     * get Report and export common Nested Data
     *
     * @param  mixed $history
     * @return void
     */
    public function getReportNestedData($asset)
    {
        $nestedData['report']['asset_tag'] = $nestedData['export']['Asset Tag #'] = $asset->asset->asset_tag;
        $nestedData['report']['serial_number'] = $nestedData['export']['Serial #'] = $asset->asset->serial_no;
        $nestedData['report']['type'] = $nestedData['export']['Asset Type'] = optional($asset->asset->assetType)->name;
        $nestedData['report']['hardware_standard'] = $nestedData['export']['Hardware Standard'] = optional($asset->asset->makeAndModel)->name;
        $nestedData['report']['asset_status'] = $nestedData['export']['Asset Status'] = optional($asset->newStatus)->name;
        $nestedData['report']['user_location'] = $nestedData['export']['User/Location'] = $asset->asset->user ? ($asset->asset->user->first_name . ' ' . $asset->asset->user->last_name) : ($asset->asset->location ? $asset->asset->location->room_name : '');

        if ($asset->asset->user && $asset->asset->location) {
            $nestedData['report']['user_location'] = $nestedData['export']['User/Location'] = ($asset->asset->user->first_name . ' ' . $asset->asset->user->last_name . ' / ' . $asset->asset->location->room_name);
        }
        $dateDamaged =  $this->getDateDamaged($asset->asset);
        $dateRepaired = $this->getDateRepaired($asset->asset);
        $nestedData['report']['date_damaged'] = $nestedData['export']['Date Damaged'] = $dateDamaged;
        $nestedData['report']['date_repaired'] = $nestedData['export']['Date Repaired'] = $dateRepaired;
        $nestedData['report']['days_to_repair'] = $nestedData['export']['Days To Repair'] = $this->getDaysToRepair($dateDamaged, $dateRepaired);
        $nestedData['report']['age'] = $nestedData['export']['Asset Age'] = $asset->asset->AssetAge;
        $nestedData['report']['depreciated_value'] = $nestedData['export']['Depreciated Value'] = config('currency.currency_symbol') . number_format($asset->asset->depreciatedValue);
        $nestedData['report']['warranty_end_date'] = $nestedData['export']['Warranty End Date'] = $asset->asset->warranty_end_date;
        $nestedData['report']['last_modified_date'] = $nestedData['export']['Last Modified Date'] = $asset->asset->latestAssetHistory ?  $asset->asset->latestAssetHistory->updated_at : '';
        $nestedData['report']['last_modified_by'] = $nestedData['export']['Last Modified By'] = $asset->asset->latestAssetHistory ? optional($asset->asset->latestAssetHistory->user)->user_name : '';

        return $nestedData;
    }

    /**
     * nested data for report
     *
     * @return array
     */
    public function getNestedData($asset, $index)
    {
        $nestedData = [];
        $commonData = $this->getReportNestedData($asset);
        $nestedData = $commonData['report'];
        $nestedData['id']   = $index;
        $nestedData['asset_tag'] = generateAssetLink($asset->asset->id, $asset->asset->asset_tag);
        $nestedData['serial_number'] = generateAssetLink($asset->asset->id, $asset->asset->serial_no);

        $userAndLocation = '';
        if ($asset->asset->user) {
            $userAndLocation = generateUserLink($asset->asset->user?->id, $asset->asset->user?->user_name);
        }

        if ($asset->asset->location) {
            $userAndLocation = generateLocationLink($asset->asset->location->id, $asset->asset->location->room_name);
        }

        if ($asset->asset->user && $asset->asset->location) {
            $userAndLocation .= ' / ' . generateLocationLink($asset->asset->location->id, $asset->asset->location->room_name);
        }
        $nestedData['user_location'] = $userAndLocation;

        return $nestedData;
    }


    /**
     * Data to widgets in top
     *
     * @return array
     */
    public function getWidgetCountData($dateFrom, $dateTo)
    {
        $assets = $this->repository->getRepairAndRecoveryAssets();
        $damagedAssets = $this->repository->getTotalDamagedAssets();
        $repairedPendingAssets = $this->repository->getRepairedPendingAssets();

        if (request()->ajax()) {
            $assets = $this->filterWithInputData($assets);
            $damagedAssets = $this->filterWithInputData($damagedAssets);
            $repairedPendingAssets = $this->filterWithInputData($repairedPendingAssets);
        } else {
            $assets = $this->commonFilterRepository->filterWithDirectDateRange($assets, 'created_at', $dateFrom, $dateTo);
            $damagedAssets = $this->commonFilterRepository->filterWithDirectDateRange($damagedAssets, 'created_at', $dateFrom, $dateTo);
            $repairedPendingAssets = $this->commonFilterRepository->filterWithDirectDateRange($repairedPendingAssets, 'created_at', $dateFrom, $dateTo);
        }
        
        $count['assets'] = $damagedAssets->count();
        $count['assets_repaired'] = $assets->count();        
        $count['assets_pending'] = $repairedPendingAssets->count();
        $count['average_days_to_repair'] = $this->getAverageDaysToRepair($count['assets_repaired'], $dateFrom, $dateTo);

        return $count;
    }

    /**
     * Retrieves the average days to repair assets.
     *
     * @param count $assetsRepairedCount
     * @param date $dateFrom
     * @param date $dateTo
     * @return count
     */
    public function getAverageDaysToRepair($assetsRepairedCount, $dateFrom, $dateTo)
    {
        $averageDaysToRepair = 0;
        $daysTaken = abs($this->repository->retrieveDaysTakenRepairAssets($dateFrom, $dateTo));
        if (!is_null($assetsRepairedCount) && $assetsRepairedCount > 0) {
            $averageDaysToRepair = $daysTaken / $assetsRepairedCount;
        }

        return round($averageDaysToRepair);
    }

    /**
     * Retrieves the date from the request or defaults to the 30 days before current date.
     *
     * @return Carbon The date retrieved from the request or the default date.
     */
    public function getFromDate()
    {
        return request()->has('date_from') ? request('date_from') : thirty_days_before_current_date();
    }

    /**
     * Retrieves the "to date" from the given request or defaults to the current day
     *
     * @return \Carbon\Carbon The "to date" value.
     */
    public function getToDate()
    {
        return request()->has('date_to') ? request('date_to') : Carbon::now()->format(config('date.formats.read_date_format'));
    }

    /**
     * retrieve the created date of the latest asset histories entry where the status was changed to damaged
     *
     * @param $asset
     *
     * @return date
     */
    public function getDateDamaged($asset)
    {
        $date = $this->repository->getDateDamaged($asset);
        if ($date) {
            return parse_date_from_db_datetime($date->getRawOriginal('created_at'));
        }
        return '';
    }

    /**
     * retrieve the created date of the latest asset histories entry where the status was changed to repaired statuses
     *
     * @param $asset
     *
     * @return date
     */
    public function getDateRepaired($asset)
    {
        $date = $this->repository->getDateRepaired($asset);
        if ($date) {
            return parse_date_from_db_datetime($date->getRawOriginal('created_at'));
        }
        return '';
    }

    /**
     * return the total days that taken by an asset from damaged to repaired status
     *
     * @param $asset
     *
     * @return date
     */
    public function getDaysToRepair($dateDamaged, $dateRepaired)
    {
        if ($dateDamaged != '' && $dateRepaired != '') {
            $dateDamaged = Carbon::parse($dateDamaged);
            $dateRepaired = Carbon::parse($dateRepaired);
            return $dateDamaged->diff($dateRepaired)->days;
        }
        return '';
    }
}
