<?php

namespace App\Services\Revivn;

use Carbon\Carbon;
use Illuminate\Support\Facades\Storage;
use App\Models\RevivnAsset;
use App\Models\RevivnPickup;
use App\Models\Asset;
use App\Models\AssetHistory;
use App\Models\AssetStatus;
use App\Models\AssetType;
use App\Models\MakeAndModel;
use App\Models\TechnicalSpecs;
use Illuminate\Support\Facades\Log;
use Exception;
use Cache;
use Illuminate\Support\Facades\DB;

class RevivnSyncService
{
    protected $repository;

    public function __construct()
    {
        DB::statement('SET FOREIGN_KEY_CHECKS=0;');
        $this->disposedStatusId = Cache::remember('eol-status-id', 720, function () {
            $value = optional(AssetStatus::getStatusId('end_of_life_data_wiped')->first())->id;
            if (is_null($value)) {
                Cache::forget('eol-status-id');
                Log::warning('Cache for eol-status-id was empty and has been cleared.');
            }
            return $value;
        });

        $this->statusId = Cache::remember('end-of-life-id', 720, function () {
            $value = optional(AssetStatus::getStatusId('end_of_life')->first())->id;
            if (is_null($value)) {
                Cache::forget('end-of-life-id');
                Log::warning('Cache for end-of-life-id was empty and has been cleared.');
            }
            return $value;
        });

        $this->assetType = Cache::remember('eol-asset-type', 720, function () {
            $value = AssetType::firstOrCreate(['slug' => 'eol_asset_codd', 'name' => 'EOL Asset - CODD'])->id;
            if (is_null($value)) {
                Cache::forget('eol-asset-type');
                Log::warning('Cache for eol-asset-type was empty and has been cleared.');
            }
            return $value;
        });

        $this->makeAndModel = Cache::remember('eol-make-and-model', 720, function () {
            $assetTypeId = $this->assetType;
            $value = MakeAndModel::firstOrCreate([
                'slug' => 'eol_asset_codd',
                'name' => 'EOL Asset - CODD',
                'asset_type_id' => $assetTypeId
            ])->id;
            if (is_null($value)) {
                Cache::forget('eol-make-and-model');
                Log::warning('Cache for eol-make-and-model was empty and has been cleared.');
            }
            return $value;
        });

        $this->technicalSpecs = Cache::remember('eol-technical-specs', 720, function () {
            $makeAndModelId = $this->makeAndModel;
            $value = TechnicalSpecs::firstOrCreate([
                'details' => 'EOL Asset - CODD',
                'make_and_model_id' => $makeAndModelId
            ])->id;
            if (is_null($value)) {
                Cache::forget('eol-technical-specs');
                Log::warning('Cache for eol-technical-specs was empty and has been cleared.');
            }
            return $value;
        });
    }

    /**
     * Creates Revivn data based on the given input data.
     *
     * @param mixed $data The input data.
     * @return string The IDs of the created Revivn assets.
     */
    public function createPickupData($data)
    {
        $pickupData = $this->getRevivnPickupData($data);
        RevivnPickup::updateOrCreate(
            ['pickup_id' => $pickupData['pickup_id']],
            $pickupData
        );
        return true;
    }

    /**
     * A description of the entire PHP function.
     *
     * @param datatype $paramname description
     * @throws Some_Exception_Class description of exception
     * @return Some_Return_Value
     */
    public function getRevivnPickupData($pickupbj)
    {
        $pickupData['pickup_id'] = $pickupbj->id;
        $pickupData['order_number'] = $pickupbj->order_number;
        $pickupData['state'] = $pickupbj->state;
        $pickupData['comments'] = $pickupbj->comments;
        $pickupData['confirmed_time'] = $pickupbj->confirmed_time;
        $pickupData['scheduled_date'] = $pickupbj->scheduled_date;

        return $pickupData;
    }

    /**
     * Creates Revivn asset data based on the given input data.
     *
     * @param array $data The input data.
     * @return string The IDs of the created Revivn assets.
     */
    public function createRevivnAssetData($data)
    {
        foreach ($data as $assetObj) {

            $assetId = $this->getAssetId($assetObj->serial_number);
            $asset = Asset::with('revivnAsset')->find($assetId);
            $revivnAssetData = $this->getRevivnAssetData($assetObj, $assetId);
            $alreadyUpdated = $this->getAlreadyUpdated($asset, $revivnAssetData);

            RevivnAsset::updateOrCreate(
                ['serial_no' => $revivnAssetData['serial_no']],
                $revivnAssetData
            );

            if ($alreadyUpdated == 1) {
                continue;
            }

            $ewasteCertificate = $this->downloadFile($revivnAssetData['cod_url'], $revivnAssetData['serial_no']);

            if ($asset) {
                $this->createAssetHistory($asset);
                $this->updateAsset($asset, $ewasteCertificate);
            }
        }

        return true;
    }

    /**
     * Retrieves the asset ID based on the provided serial number.
     *
     * @param string $serialNumber The serial number of the asset.
     * @param string $revivnAssetData The revivnAssetData.
     * @throws \Exception If the asset does not exist and cannot be created.
     * @return int The asset ID.
     */
    public function getAssetId($serialNumber)
    {
        $asset = Asset::where('serial_no', $serialNumber)->first();
        if (!$asset) {
            $asset = $this->createAsset($serialNumber);
        }

        return $asset->id;
    }

    /**
     * Get the status of whether the asset has already been updated via Revivn.
     *
     * @param datatype $asset description of the asset parameter
     * @return int the status of whether the asset has already been updated (1 for true, 0 for false)
     */
    public function getAlreadyUpdated($asset, $revivnAssetData)
    {
        //already updated if asset has revivnAsset relationship and  donation certificate  
        // or asset has revivnAsset relationship and cod_url from api data is null
        if (($asset->revivnAsset && $asset->ewaste_certificate) ||
            ($asset->revivnAsset && $revivnAssetData['cod_url'] == null)
        ) {

            return 1;
        }

        return 0;
    }

    /**
     * Creates an asset with the given serial number.
     *
     * @param string $serialNumber The serial number of the asset.
     * @throws Some_Exception_Class Description of the exception that can be thrown.
     * @return Asset The newly created asset.
     */
    public function createAsset($serialNumber)
    {
        $assetData = [
            'serial_no' => $serialNumber,
            'asset_tag' => $serialNumber,
            'asset_status_id' => $this->statusId,
            'asset_type_id' => $this->assetType,
            'make_and_model_id' => $this->makeAndModel,
            'technical_spec_id' => $this->technicalSpecs,
        ];
        $asset = Asset::create($assetData);

        return $asset;
    }

    /**
     * Updates the asset with the given donation certificate and sets other related fields.
     *
     * @param Asset $asset The asset to be updated
     * @param string $ewasteCertificate The donation certificate to be assigned
     */
    public function updateAsset($asset, $ewasteCertificate)
    {
        $asset->update([
            'ewaste_certificate' => $ewasteCertificate,
            'certificate_added_date' => Carbon::now()->format('Y-m-d'),
            'asset_status_id' => $this->disposedStatusId,
            'location_id' => null,
            'user_id' => null,
        ]);
    }

    /**
     * Creates an asset history record.
     *
     * @param object $asset The asset object.
     * @throws Some_Exception_Class Description of exception.
     * @return bool Returns true if the asset history record was created successfully.
     */
    public function createAssetHistory($asset)
    {
        AssetHistory::create([
            'asset_id' => $asset->id,
            'user_id' => null,
            'old_location_id' => $asset->location_id,
            'old_user_id' => $asset->user_id,
            'old_asset_status_id' => $asset->asset_status_id,
            'new_asset_status_id' => $this->disposedStatusId,
            'action' => 'revivn_data_destruction',
            'created_by' => 'Revivn',
            'description' => __('history.CertificateUploadedRevivn', [
                'asset_name' => $asset->serial_no,
                'asset_id' => $asset->id,
            ]),
        ]);

        return true;
    }

    /**
     * Gets the Revivn asset data for the given asset object and ID.
     *
     * @param mixed $assetObj the asset object
     * @param mixed $assetId the asset ID
     * @return array the Revivn asset data
     */
    public function getRevivnAssetData($assetObj, $assetId)
    {
        $revivnAssetData = [];
        $pickup = RevivnPickup::where('pickup_id', $assetObj->pickup_id)->first();

        $revivnAssetData['asset_id'] = $assetId;
        $revivnAssetData['revivn_id'] = $assetObj->id;
        $revivnAssetData['revivn_pickup_id'] = $pickup->id;
        $revivnAssetData['asset_tag'] = $assetObj->asset_tag;
        $revivnAssetData['serial_no'] = $assetObj->serial_number;
        $revivnAssetData['model'] = $assetObj->model;
        $revivnAssetData['device_type'] = $assetObj->device_type;
        $revivnAssetData['manufacturer'] = $assetObj->manufacturer;
        $revivnAssetData['hard_drive'] = $assetObj->hard_drive;
        $revivnAssetData['ram'] = $assetObj->ram;
        $revivnAssetData['cpu'] = $assetObj->cpu;
        $revivnAssetData['year'] = $assetObj->year;
        $revivnAssetData['end_of_life_condition'] = $assetObj->end_of_life_condition;
        $revivnAssetData['status'] = $assetObj->status;
        $revivnAssetData['cod_url'] = $assetObj->cod_url;


        return $revivnAssetData;
    }

    /**
     * Downloads and stores a file from a given URL.
     *
     * @param string $codUrl The URL of the file to be downloaded and stored.
     * @throws Exception If there is an error downloading or storing the file.
     * @return string The path to the stored file.
     */
    function downloadFile($codUrl, $serial)
    {
        if ($codUrl == '') {
            return null;
        }
        try {
            if (!Storage::exists('public/eol_certificates/revivn')) {
                Storage::makeDirectory('public/eol_certificates/revivn');
            }
            // Download the file
            $fileContent = file_get_contents($codUrl);

            // Save the file locally
            $filename = $serial . '_' . time() . '.pdf';
            $storagePath = storage_path("app/public/eol_certificates/revivn/" . $filename);
            file_put_contents($storagePath, $fileContent);

            return 'eol_certificates/revivn/' . $filename;
        } catch (Exception $e) {
            Log::channel('daily')->info("revivn download error: " . $e->getMessage());

            return null;
        }
    }
}
