<?php

namespace App\Services\Api\Jira;

use Facades\App\Services\Asset\AssetStatusService;
use Facades\App\Services\AssetHistory as RepoAssetHistory;
use Facades\App\Services\JiraDescription;
use App\Http\Traits\JiraPluginTrait;
use App\Events\UpdateAssetStatus;
use App\Events\UpdateJiraIssue;
use Illuminate\Http\Request;
use App\Models\AssetStatus;
use App\Models\Asset;
use App\User;
use Cache;
use Illuminate\Support\Facades\Auth;

/**
 * Service class for the Asset status update from Jira App
 */
class TeqtivityAssetStatus
{
    use JiraPluginTrait;

    /**
     * Validate the asset status update request.
     *
     * @param Request $request The incoming request instance.
     * @return string|null The validation message or null if validation passes.
     */
    public function validate(Request $request)
    {
        $assetId  = $request->asset_id;
        $statusId = $request->status_id;

        if (empty($assetId) || empty($statusId)) {
            return 'Something went wrong. Try again later';
        }

        $status = AssetStatus::getName($statusId)->first()->name;

        $asset = Asset::with('assetStatus', 'childrenAsset')->where('id', $assetId);
        $check = AssetStatusService::canBeUpdatedTo($status, $asset);

        if (!$check) {
            return 'Do not qualify! The asset cannot be updated to the status picked';
        }

        // Checks if the location of asset is frozen or not.
        $check = AssetStatusService::validateFromLocationFreeze($asset);

        if (!$check) {
            return 'Do not qualify! cannot update the status of asset from frozen location.';
        }

        return null;
    }

    /**
     * Update the status of an asset.
     *
     * @return string A message indicating the status of the update.
     */
    public function update()
    {
        $assetId  = request('asset_id');
        $statusId = request('status_id');
        $status   = AssetStatus::findOrFail($statusId);

        $asset    = Asset::with('assetStatus', 'childrenAsset')->where('id', $assetId)->first();

        $currentStatus = $asset->assetStatus->slug;

        $data = $this->getData($currentStatus, $asset, $status);

        $description     = RepoAssetHistory::getStatusDescription($status->name, $data, $asset, '');
        $jiraDescription = JiraDescription::getStatusDescription($status->name, $data, $asset, '');
        $authUser        = $this->getAuthenticatedUser();

        $assetHistory = [
            'user_id'  => !Auth::check() ? optional($authUser)->id : Auth::id(),
            'asset_id'  => $asset->id,
            'ticket_no' => request('ticket_no'),
            'action' => 'status_updated',
            'comments' => '',
            'old_value' => $asset->assetStatus->name,
            'new_value' => $status->name,
            'description' => $description,
            'created_by' => !Auth::check() ? optional($authUser)->first_name . ' ' . optional($authUser)->last_name : '',
        ];

        $assetHistory = $this->getHistoryData($assetHistory, $data, $asset);

        event(new UpdateAssetStatus($jiraDescription, request('ticket_no'), $assetHistory, optional($authUser)->id));

        $asset->update($data);

        if ($asset->has('childrenAsset')) {
            foreach ($asset->childrenAsset as $assetChild) {
                $assetChild->update([
                    'user_id' => $data['user_id'],
                    'location_id' => $data['location_id'],
                    'asset_status_id' => $data['asset_status_id'],
                ]);
            }
        }

        return 'The asset has been updated';
    }

    /**
     * Set the user ID in the asset data array if applicable.
     *
     * @param array $assetData The asset data array.
     * @param int   $hasUser   Indicator if the asset has a user (1 if true, 0 if false).
     * @param Asset $asset     The asset model instance.
     *
     * @return array The updated asset data array.
     */
    public function arraySetUser($assetData, $hasUser, $asset)
    {
        if ($hasUser == 1) {
            if (request()->has('user_id')) {
                $assetData['user_id'] = request('user_id');
            } else {
                $assetData['user_id'] = $asset->user_id;
            }
        }

        return $assetData;
    }

    /**
     * Set the location ID in the asset data array if applicable.
     *
     * @param array $assetData   The asset data array.
     * @param int   $hasLocation Indicator if the asset has a location (1 if true, 0 if false).
     * @param Asset $asset       The asset model instance.
     *
     * @return array The updated asset data array.
     */
    public function arraySetLocation($assetData, $hasLocation, $asset)
    {
        if ($hasLocation == 1) {
            if (request()->has('location_id')) {
                $assetData['location_id'] = request('location_id');
            } else {
                $assetData['location_id'] = $asset->location_id;
            }
        }

        return $assetData;
    }

    /**
     * Set the asset data array with values from the request.
     *
     * @param array $assetData The asset data array.
     *
     * @return array The updated asset data array.
     */
    public function arraySetFromRequest($assetData)
    {
        $request = request()->only(['ticket_no', 'loaner_return_date']);

        foreach ($request as $key => $value) {
            $assetData[$key] = $value;
        }

        return $assetData;
    }

    /**
     * Set specified fields in the asset data array to null.
     *
     * @param array $assetData The asset data array.
     *
     * @return array The updated asset data array with specified fields set to null.
     */
    public function arraySetNull($assetData)
    {
        $assetData['loaner_return_date'] = null;
        $assetData['loaner_retention_date'] = null;
        $assetData['wipe_confirmation'] = null;
        $assetData['lost_date'] = null;
        $assetData['end_of_life_date'] = null;
        $assetData['location_id'] = null;
        $assetData['user_id'] = null;

        return $assetData;
    }

    /**
     * Generate the history data for asset changes.
     *
     * @param array $assetHistory The asset history data array.
     * @param array $assetData    The asset data array.
     * @param Asset $asset        The asset model instance.
     *
     * @return array The updated asset history data array.
     */
    public function getHistoryData($assetHistory, $assetData, $asset)
    {
        $keyArray = ['loaner_return_date', 'location_id', 'user_id', 'loaner_retention_date', 'asset_status_id'];

        foreach ($keyArray as $key) {
            if (array_key_exists($key, $assetData)) {
                $assetHistory['new_' . $key] = $assetData[$key];
                $assetHistory['old_' . $key] = $asset->$key;
            }
        }

        if (array_key_exists('wipe_confirmation', $assetData)) {
            $assetHistory['wipe_confirmation'] = $assetData['wipe_confirmation'];
        }

        return $assetHistory;
    }

    /**
     * Get the data for updating the asset based on the current status, asset, and new status.
     *
     * @param string      $currentStatus The current status of the asset.
     * @param Asset       $asset         The asset model instance.
     * @param AssetStatus $newStatus     The new status to be applied to the asset.
     *
     * @return array The data to be used for updating the asset.
     */
    public function getData($currentStatus, $asset, $newStatus)
    {
        $assetData['asset_status_id'] = request('status_id');

        $assetData = $this->arraySetNull($assetData);

        $hasLocation = $newStatus->has_location;
        $hasUser = $newStatus->has_user;

        $assetData = $this->arraySetUser($assetData, $hasUser, $asset);
        $assetData = $this->arraySetLocation($assetData, $hasLocation, $asset);
        $assetData = $this->arraySetFromRequest($assetData);

        return $assetData;
    }
}
