<?php

namespace App\Services\Reports;

use App\Repositories\Reports\UnrepairableRepository;
use App\Repositories\CommonFilterRepository;
use App\Http\Responses\ReportOutputData;
use App\Http\Traits\DepreciationValueTrait;
use Carbon\Carbon;

/**
 * Service class for Unrepairable Report
 */
class UnrepairableService extends AbstractReportService
{
    use DepreciationValueTrait;

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

    /**
     * Construct of UnrepairableService
     * @param UnrepairableRepository $repository The repository for the InStockComputer Repository entity
     * @param CommonFilterRepository $commonFilterRepository The repository for the CommonFilter entity
     * @param ReportOutputData $reportOutputData The ReportOutputData entity
     */
    public function __construct(UnrepairableRepository $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()
    {
        $result = $this->repository->getUnrepairableAssets();
        $result = $this->filterWithInputData($result);
        $count  = $result->count();
        $result = $this->reportOutputData->getOutputData($result, ['id' => 'asc']);

        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()->form : request()->all();
        $assets = $this->commonFilterRepository->filterWithDirectFields($assets, 'assets.asset_type_id', $requestedData['asset_type'] ?? '');
        $assets = $this->commonFilterRepository->filterWithDirectFields($assets, 'asset_status_id', $requestedData['status'] ?? '');
        $assets = $this->commonFilterRepository->filterWithDirectFields($assets, 'assets.make_and_model_id', $requestedData['make_and_model'] ?? '');
        $assets = $this->commonFilterRepository->filterWithDirectFields($assets, 'location_id', $requestedData['location'] ?? '');
        $assets = $this->commonFilterRepository->filterWithDirectFields($assets, 'technical_spec_id', $requestedData['tech_spec'] ?? '');
        $assets = $this->commonFilterRepository->filterWithWhereHasRelationFields($assets, 'makeAndModel.manufacturer', 'id', $requestedData['manufacturer'] ?? '');
        $assets = $this->commonFilterRepository->filterWithRelationalDateRange($assets, 'assetHistory', 'created_at', $requestedData['date_from'] ?? '', $requestedData['date_to'] ?? '');

        return $assets;
    }

    /**
     * export Data as csv
     *
     * @return mixed
     */
    public function exportData()
    {
        $assets = $this->repository->getUnrepairableAssets();
        $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_tag;
        $nestedData['report']['serial_number'] = $nestedData['export']['Serial #'] = $asset->serial_no;
        $nestedData['report']['type'] = $nestedData['export']['Asset Type'] = $asset->assetType ? $asset->assetType->name : '';
        $nestedData['report']['hardware_standard'] = $nestedData['export']['Hardware Standard'] = optional($asset->makeAndModel)->name;
        $nestedData['report']['tech_specs'] = $nestedData['export']['Technical Specs'] = optional($asset->technicalSpec)->details;
        $nestedData['report']['asset_status'] = $nestedData['export']['Asset Status'] = $asset->assetStatus ? $asset->assetStatus->name : '';
        $nestedData['report']['user_location'] = $nestedData['export']['User/Location'] = $asset->user ? ($asset->user->first_name . ' ' . $asset->user->last_name) : ($asset->location ? $asset->location->room_name : '');

        if ($asset->user && $asset->location) {
            $nestedData['report']['user_location'] = $nestedData['export']['User/Location'] = ($asset->user->first_name . ' ' . $asset->user->last_name . ' / ' . $asset->location->room_name);
        }
        $dateDamaged =  $this->getDateDamaged($asset);
        $dateUpdated = $this->getDateUpdated($asset);
        $nestedData['report']['date_damaged'] = $nestedData['export']['Date Damaged'] = $dateDamaged;
        $nestedData['report']['date_updated'] = $nestedData['export']['Date Updated'] = $dateUpdated;
        $nestedData['report']['days_to_review'] = $nestedData['export']['Days to Review'] = $this->getDaysToReview($dateDamaged, $dateUpdated);
        $nestedData['report']['age'] = $nestedData['export']['Asset Age'] = $asset->AssetAge;
        $nestedData['report']['depreciated_value'] = $nestedData['export']['Depreciated Value'] = config('currency.currency_symbol') . number_format($asset->depreciatedValue);
        $nestedData['report']['warranty_end_date'] = $nestedData['export']['Warranty End Date'] = $asset->warranty_end_date;
        $nestedData['report']['modified_date'] = $nestedData['export']['Last Modified Date'] = $asset->latestAssetHistory ? $asset->latestAssetHistory->updated_at : '';
        $nestedData['report']['modified_by'] = $nestedData['export']['Last Modified By'] = $asset->latestAssetHistory ? (optional($asset->latestAssetHistory->user)->first_name . ' ' . optional($asset->latestAssetHistory->user)->last_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->id, $asset->asset_tag);
        $nestedData['serial_number'] = generateAssetLink($asset->id, $asset->serial_no);

        $userAndLocation = '';

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

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

        return $nestedData;
    }

    /**
     * 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') ? format_date_to_carbon(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') ? format_date_to_carbon(request('date_to')) : Carbon::now()->format(config('date.formats.read_date_format'));
    }

    /**
     * Get widget counts
     * @param date $dateFrom
     * @param date $dateTo
     *
     * @return [type]
     */
    public function getWidgetCountData($dateFrom, $dateTo)
    {
        $assets = $this->repository->getUnrepairableAssets();
        $damagedAssets = $this->repository->getTotalDamagedAssets();
        $assetsUnderWarranty = $this->repository->getAssetsUnderWarranty($dateFrom);

        if (request()->ajax()) {
            $assets = $this->filterWithInputData($assets);
            $damagedAssets = $this->filterWithInputData($damagedAssets);
            $assetsUnderWarranty = $this->filterWithInputData($assetsUnderWarranty);
        } else {
            $assets = $this->commonFilterRepository->filterWithRelationalDateRange($assets, 'assetHistory', 'created_at', $dateFrom, $dateTo);
            $damagedAssets = $this->commonFilterRepository->filterWithRelationalDateRange($damagedAssets, 'assetHistory', 'created_at', $dateFrom, $dateTo);
            $assetsUnderWarranty = $this->commonFilterRepository->filterWithRelationalDateRange($assetsUnderWarranty, 'assetHistory', 'created_at', $dateFrom, $dateTo);
        }

        $count['assets'] = $damagedAssets->count();
        $count['assets_unrepaired'] = $assets->count();
        $count['assets_value'] = config('currency.currency_symbol'). number_format($this->getDepreciationValue($assets));
        $count['assets_under_warranty'] = $assetsUnderWarranty->count();
        $count['average_days_to_repair'] = $this->getAverageDaysToUnrepair($count['assets_unrepaired'], $dateFrom, $dateTo);
        return $count;
    }

    /**
     * Get average days that an asset is in unrepaired stage
     * @param int $assetsUnrepairedCount
     * @param date $dateFrom
     * @param date $dateTo
     *
     * @return int
     */
    public function getAverageDaysToUnrepair($assetsUnrepairedCount, $dateFrom, $dateTo)
    {
        $averageDaysToUnrepair = 0;
        $daysTaken = abs($this->repository->retrieveDaysTakenToUnrepairAssets($dateFrom, $dateTo));

        if (!is_null($assetsUnrepairedCount) && $assetsUnrepairedCount > 0) {
            $averageDaysToUnrepair = $daysTaken / $assetsUnrepairedCount;
        }
        return round($averageDaysToUnrepair);
    }

    /**
     * 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 un repaired statuses
     *
     * @param $asset
     *
     * @return date
     */
    public function getDateUpdated($asset)
    {
        $date = $this->repository->getDateUpdated($asset);
        if ($date) {
            return parse_date_from_db_datetime($date->getRawOriginal('created_at'));
        }
        return '';
    }

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