<?php

namespace App\Services\Reports;

use App\Models\Asset;
use App\Models\ReportHistoryVarience;
use Illuminate\Support\Facades\Auth;

/**
 * Service Class for Variance Report
 *
 * Assets Defference between the physical location and Database location
 */
class Variance
{
    /**
     * Get all scanned asset details
     *
     * @param array  $assetTags    Scanned asset tags
     * @param object $location     App\Models\Location object
     *
     * @return array()
     */
    public function getScannedAssetData($assetTags, $location)
    {
        $assets = Asset::with('makeAndModel.manufacturer', 'technicalSpec', 'assetStatus', 'location', 'user')->whereIn('asset_tag', $assetTags)->orWhereIn('serial_no', $assetTags)->get();

        $datas = $assets->mapWithKeys(function ($item, $key) use ($location) {
            $match = $item['location_id'] == $location->id ? 'Y' : 'N';

            $discrepancy = $this->getDiscrepancy($item, $match);

            return [
                $key => $this->getKeyData($item, $match, $discrepancy)
            ];
        });
        return $datas;
    }

    /**
     * Get not scanned asset details from the given location
     *
     * @param array  $assetTags    Scanned asset tags
     * @param object $location     App\Models\Location object
     *
     * @return array()
     */
    public function getNotScannedAssetData($assetTags, $location)
    {
        $assetsNotScanned = Asset::with('makeAndModel.manufacturer', 'technicalSpec', 'assetStatus', 'location', 'user')->assetNotScannedInLocation($assetTags, $location->id);
        $datasNotScanned = collect([]);
        $assetsNotScanned->chunk(10000, function ($assets) use (&$datasNotScanned, $location) {
            $datasNotScanned = $datasNotScanned->merge($assets->mapWithKeys(function ($item, $key) use ($location) {

                $discrepancy = 'Not scanned';
                $match = 'N';

                return [
                    $key => $this->getKeyData($item, $match, $discrepancy)
                ];
            }));
        });
        return $datasNotScanned;
    }

    /**
     * Get scanned but not in the databse asset details
     *
     * @param array  $assetTags    Scanned asset tags
     * @param object $location     App\Models\Location object
     *
     * @return array()
     */
    public function getNotInDatabaseAssetData($assetTags, $location)
    {
        $existingAssetTags = Asset::whereIn('asset_tag', $assetTags)->get()->pluck('asset_tag')->toArray();
        $existingSerialNumbers = Asset::whereIn('serial_no', $assetTags)->get()->pluck('serial_no')->toArray();
        $existingAssets = array_merge($existingAssetTags, $existingSerialNumbers);
        $notExistingAssets = array_diff($assetTags, $existingAssets);
        $dataNotInDatabase = array();
        foreach ($notExistingAssets as $tag) {
            $discrepancy = 'Scanned but not present';
            $match = 'N';
            $dataNotInDatabase[] = $this->getNotInDbData($tag, $location['room_name'], $match, $discrepancy);
        }

        return $dataNotInDatabase;
    }

    /**
     * Get discrepancy string of an asset after scanning
     *
     * @param object  $item   App\Models\Asset Object
     * @param string  $match  Conditions used for matched or not (Y or N)
     *
     * @return string
     */
    public function getDiscrepancy($item, $match)
    {
        $discrepancy = '';
        if ($match == 'N') {
            $discrepancy = 'Asset is currently ' . $item->assetStatus['name'];
            if ($item->location) {
                $discrepancy = $discrepancy . ' at ' . $item->location['room_name'];
            }

            if ($item->user) {
                $discrepancy = $discrepancy . ' to ' . $item->user['first_name'] . ' ' . $item->user['last_name'];
            }
        }
        return $discrepancy;
    }

    /**
     * Generating Asset Details to Show in Vairance Report generating page
     *
     * @param object  $item         Asset Object
     * @param string  $match        Asset Matched/Not with location (Y or N)
     * @param string  $discrepancy  Descrepency string
     *
     * @return array()
     */
    public function getKeyData($item, $match, $discrepancy)
    {
        return [
            'asset_tag' => $item['asset_tag'],
            'hardware_standard' => $item->makeAndModel ? $item->makeAndModel->makeModelName : '',
            'technical_spec' => !empty($item->technicalSpec) ? $item->technicalSpec->details : '',
            'serial_no' => $item['serial_no'],
            'status' => optional($item->assetStatus)->name,
            'location' => optional($item->location)->room_name,
            'location_match' => $match,
            'discrepancy' => $discrepancy,
        ];
    }

    /**
     * Generating Not in the Database Asset Details to Show in Vairance Report generating page
     *
     * @param string  $assetTag     Asset Tag
     * @param string  $location     Location of the Asset
     * @param string  $match        Asset Matched/Not with location (Y or N)
     * @param string  $discrepancy  Descrepency string
     *
     * @return array()
     */
    public function getNotInDbData($assetTag, $location, $match, $discrepancy)
    {
        return [
            'asset_tag' => $assetTag,
            'hardware_standard' => '',
            'technical_spec' => '',
            'serial_no' => '',
            'status' => '',
            'location' => $location,
            'location_match' => $match,
            'discrepancy' => $discrepancy,
        ];
    }

    /**
     * Create a csv file with the generated result and save on server
     * 
     * @param array $datasScanned        Scanned asset details
     * @param array $datasNotScanned     Not scanned asset details
     * @param array $dataNotInDatabase   Scanned but not in the system asset details
     */
    public function generateCsvFile($datasScanned, $datasNotScanned, $dataNotInDatabase, $assetsMatched, $assetsNotMatched, $variance, $reportName, $createdDate, $userName)
    {
        $reportName = str_replace(' ', '_', $reportName) . '.csv';
        $reportName = str_replace(':', '', $reportName) . '.csv';
        $fileName = "variance_reports/" . $reportName; //for saving to DB
        $fileLocation = storage_path("app/public/variance_reports/" . $reportName);
        $file = fopen($fileLocation, 'w');

        fputcsv($file, [
            '#', 'Scanned Asset Tag #', 'Hardware Standard', 'Tech Spec', 'Serial #', 'Asset Status', 'Asset Location', 'Match at Location (Y/N)', 'Discrepancy'
        ]);

        $counter = 1;
        foreach ($datasScanned as $key => $value) {
            array_unshift($value, $counter);
            fputcsv($file, $value);
            $counter++;
        }

        foreach ($dataNotInDatabase as $key => $value) {
            array_unshift($value, $counter);
            fputcsv($file, $value);
            $counter++;
        }

        foreach ($datasNotScanned as $key => $value) {
            array_unshift($value, $counter);
            fputcsv($file, $value);
            $counter++;
        }

        fputcsv($file, []);
        fputcsv($file, ['0' => 'Date', '1' => $createdDate]);
        fputcsv($file, ['0' => 'Scanned by', '1' => $userName]);
        fputcsv($file, ['0' => 'Match Y#', '1' => $assetsMatched]);
        fputcsv($file, ['0' => 'Total Assets #', '1' => $assetsNotMatched]);
        fputcsv($file, ['0' => 'Variance #', '1' => $variance . '%']);

        fclose($file);

        return $fileName;
    }

    /**
     * Generate Variance History page content
     *
     * @param request
     */
    public function historyFilter()
    {
        $histories = new ReportHistoryVarience;
        if (isset(request('form')['location_id'])) {
            $histories = $histories->where('location_id', request('form')['location_id']);
        }
        $count = $histories->get()->count();
        $histories = $this->getOutputData($histories);
        return compact('histories', 'count');
    }

    /**
     * Adding Limit to the query (Datatable pagination) and take the result
     *
     * @param object $histories    App\Models\ReportHistoryVarience;
     */
    public function getOutputData($histories)
    {
        $start = request('start');
        $limit = request('length');

        if ($limit != -1) {
            $histories = $histories->offset($start)
                ->limit($limit);
        }
        return $histories->orderBy('created_at', 'desc')->get();
    }

    /**
     * After Fetching the data from Db, making the output for Data table
     *
     * @param object $histories    App\Models\ReportHistoryVarience;
     * @param integer $start       pagination starting index 
     *
     */
    public function historyFilterData($histories, $start)
    {
        $parentIndex = $start;
        $data = [];

        foreach ($histories as $key => $history) {
            $parentIndex++;

            $nestedData = $this->getNestedData($history, $parentIndex);
            $data[] = $nestedData;
        }

        return $data;
    }

    /**
     * Generating an array for data table fields
     *
     * @param object $history   App\Models\ReportHistoryVarience;
     * @param integer $index    Serial number
     *
     * @return array()
     */
    public function getNestedData($history, $index)
    {
        $historyDetailslink = route('variance.history.details', $history->id);
        $nestedData['id'] = $index;
        $nestedData['room_name'] = "<a href=$historyDetailslink>$history->location_name</a>";
        $nestedData['date'] = $history->date;
        $nestedData['variance'] = $history->variance . '%';
        return $nestedData;
    }

    /**
     * Data to displaying Variance Accuracy graph
     *
     * @param int request('location_id')     location_id
     *
     * @return array()
     */
    public function getAccuracyGraphData()
    {
        $lowestQuery = new ReportHistoryVarience;
        $heighestQuery = new ReportHistoryVarience;

        $lowestQuery = $this->accuracyGraphDataFilter($lowestQuery);
        $heighestQuery = $this->accuracyGraphDataFilter($heighestQuery);

        $average = $lowestQuery->avg('variance');
        $history = $heighestQuery->get();
        $highest = $heighestQuery->orderBy('variance', 'DESC')->first();
        $lowest = $lowestQuery->orderBy('variance', 'ASC')->first();
        return ['average' => $average, 'highest' => $highest, 'lowest' => $lowest, 'history' => $history];
    }

    /**
     * Adding filter option to Acuracy graph query
     *
     * @param $query
     *
     * @return $query
     */
    public function accuracyGraphDataFilter($query)
    {
        if (request('location_id'))
            $query = $query->where('location_id', request('location_id'));

        return $query;
    }
}
