<?php

namespace App\Services\Asset;

use App\Events\BulkUpdates;
use App\Models\Asset;
use App\Models\AssetTracking;
use App\Models\AssetTypeAttributeValue;
use App\Models\Location;
use App\Services\Integrations\Tickets\TicketManagementService;
use App\User;
use Carbon\Carbon;
use Exception;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;

class CreateAssetService
{
    /**
     * Initializes the class with a TicketManagementService instance.
     *
     * @param TicketManagementService $ticketManagementService The service for managing tickets.
     */
    public function __construct(
        protected TicketManagementService $ticketManagementService,
        protected AssetStatusService $assetStatusService
    ) {}

    /**
     * Format the given asset tag to have a leading zero if necessary.
     *
     * The function takes an asset tag as a parameter and checks if it is
     * empty. If the tag is empty, the function returns null. Otherwise, it
     * checks if the tag length is less than 6. If the length is less than 6,
     * the function adds a leading zero to the tag and increments the length.
     * The function then returns the formatted asset tag.
     *
     * @param string $assetTag The asset tag to be formatted.
     *
     * @return string|null The formatted asset tag or null if the tag is empty.
     */
    public function getAssetTag($assetTag)
    {
        if (!$assetTag) {
            return null;
        }
        $assetTagLength = strlen($assetTag);

        while ($assetTagLength < 6) {
            $assetTag = '0' . $assetTag;
            $assetTagLength++;
        }

        return $assetTag;
    }

    /**
     * Creates new assets based on the provided input data.
     *
     * Iterates over the input data to create or update assets. The function
     * also handles the creation of asset history and additional attributes,
     * and it generates comments for Jira tickets.
     *
     * @param array $inputData The input data containing asset information.
     * @param string|null $createdBy The user who is creating the assets.
     * 
     * @return int The number of assets successfully created or updated.
     */
    public function createNewAsset($inputData, $createdBy = null)
    {
        $jiraData = [];

        $count = 0;

        $assetStatus = $this->assetStatusService->getStatusForNewAsset();

        for ($i = 1; $i <= $inputData['count']; $i++) {

            if ($this->validateAssetData($inputData, $i) === false) {
                continue;
            }

            if (isset($inputData['serial_no' . $i]) === false) {
                continue;
            } //row was deleted before submitting

            try {

                $data = $this->getData($inputData, $i);

                $data['asset_status_id'] = $assetStatus->id;

                $asset = Asset::updateOrCreate(['serial_no' => $data['serial_no']], $data);

                // Insert Additional attributes values
                $this->saveAdditionalAttributes($inputData, $i, $asset->id);

                $this->createAssetHistory($asset, $data, $createdBy);

                $jiraData[] = $this->getTicketComments($asset, $data);

                $count++;
            } catch (Exception $e) {
                Log::channel('daily')->error("Error while creating asset: " . $e->getMessage());
                continue;
            }
        }

        session(['count_add' => 0]);

        $this->ticketManagementService->bulkAddComment($jiraData);

        return $count;
    }

    /**
     * Validate the input data for a single asset.
     *
     * Iterates over the provided input data and checks if the required fields
     * are present and valid. The fields are location_id, asset_type_id,
     * asset_tag, serial_no, ticket_no, po_id, make_and_model_id,
     * technical_spec_id and vendor_id.
     *
     * @param array $inputData The input data to be validated.
     * @param  $index The index of the asset being validated.
     *
     * @return bool True if the validation succeeds, false otherwise.
     */
    private function validateAssetData(array $inputData, $index = '')
    {
        $rules = [
            "location_id{$index}"       => ['required', 'exists:locations,id'],
            "asset_type_id{$index}"     => ['required', 'exists:asset_types,id'],
            "asset_tag{$index}"         => ['required', 'unique:assets,asset_tag'],
            "serial_no{$index}"         => ['required', 'unique:assets,serial_no'],
            // "ticket_no{$index}"         => ['required'],
            "po_id{$index}"             => ['required'],
            "make_and_model_id{$index}" => ['required', 'exists:make_and_models,id'],
            "technical_spec_id{$index}" => ['required', 'exists:technical_specs,id'],
            "vendor_id{$index}"         => ['nullable', 'exists:vendors,id'],
        ];

        $validator = Validator::make($inputData, $rules);

        if ($validator->fails()) {
            return false;
        }

        return true;
    }


    /**
     * Returns an array of asset data from the provided input data.
     * 
     * @param array $inputData The input data containing asset information.
     * @param string $count The count of the current asset being processed.
     * 
     * @return array The asset data.
     */
    public function getData(array $inputData, $count = '')
    {
        $assetData = [
            'location_id'          => $inputData['location_id' . $count] ?? null,
            'asset_type_id'        => $inputData['asset_type_id' . $count] ?? null,
            'asset_tag'            => $inputData['asset_tag' . $count] ?? null,
            'po_id'                => $inputData['po_id' . $count] ?? null,
            'vendor_id'            => $inputData['vendor_id' . $count] ?? null,
            'technical_spec_id'    => ($inputData['technical_spec_id' . $count] ?? '') == '--Select--' ? null : $inputData['technical_spec_id' . $count],
            'serial_no'            => isset($inputData['serial_no' . $count]) ? stripS($inputData['serial_no' . $count]) : null,
            'ticket_no'            => $inputData['ticket_no' . $count] ?? null,
            'make_and_model_id'    => ($inputData['make_and_model_id' . $count] ?? '') == '--Select--' ? null : $inputData['make_and_model_id' . $count],
            'asset_status_id'      => $inputData['asset_status_id' . $count] ?? null,
            'carrier_id'           => $inputData['carrier_id' . $count] ?? null,
            'imei'                 => $inputData['imei' . $count] ?? null,
            'lease_start_date'     => $inputData['lease_start_date' . $count] ?? null,
            'lease_end_date'       => $inputData['lease_end_date' . $count] ?? null,
            'warranty_end_date'    => $inputData['warranty_end_date' . $count] ?? null,
            'asset_original_value' => $inputData['asset_original_value' . $count] ?? null,
            'asset_tracking_id'    => $this->getAssetTrackingId($inputData['asset_tracking_id' . $count] ?? '')
        ];

        return $assetData;
    }

    /**
     * Retrieve or create an AssetTracking record and return its ID.
     *
     * @param string $trackingNumber
     * @return int|null
     */
    private function getAssetTrackingId(string $trackingNumber): ?int
    {
        if (empty($trackingNumber)) {
            return null;
        }

        return AssetTracking::firstOrCreate(
            ['tracking_number' => $trackingNumber]
        )->id ?? null;
    }

    /**
     * Saves extra attributes for a given asset
     *
     * @param int $count Row number from the bulk upload form
     * @param int $assetId The id of the asset to save the extra attributes for
     * @return void
     */
    public function saveAdditionalAttributes($inputData, $count, $assetId)
    {

        foreach (array_keys($inputData) as $key) {
            if (starts_with($key, 'extraAttributes') && $count == explode('_', $key)[2]) {

                $attributeValues['asset_id'] = $assetId;
                $attributeValues['value'] = $inputData[$key];
                $attributeValues['asset_type_attribute_id'] = explode('_', $key)[1];

                AssetTypeAttributeValue::updateorCreate($attributeValues);
            }
        }

        return true;
    }

    /**
     * Creates a history record for a newly created asset.
     *
     * Generates a description of the asset creation event and constructs
     * an asset history entry, including details such as user ID, asset ID,
     * ticket information, and location. Triggers a BulkUpdates event
     * with the asset history data.
     *
     * @param mixed $asset The asset instance being created.
     * @param array $data Additional data related to the asset, such as location.
     * @param string|null $createdBy The user who is creating the asset.
     *
     * @return void
     */
    private function createAssetHistory($asset, $data, $createdBy = null)
    {
        $description = __('history.Created', [
            'assetname' => $asset->serial_no,
            'assetid' => $asset->id,
            'newroomname' => optional(Location::select('id', 'room_name')->findOrFail($data['location_id']))->room_name,
        ]);

        $assetHistory = [
            'user_id' => Auth::id(),
            'asset_id' => $asset->id,
            'ticket_no' => $data['ticket_no'] ?? null,
            'ticket_service_provider' => config('ticket-integration.service'),
            'action' => 'created',
            'new_location_id' => $data['location_id'],
            'description' => $description,
            'created_at'  => Carbon::now()->format('Y-m-d H:i:s'),
            'updated_at'  => Carbon::now()->format('Y-m-d H:i:s'),
            'created_by' => $createdBy
        ];
        event(new BulkUpdates($assetHistory));
    }

    /**
     * Constructs a Jira description for a newly created asset and
     * returns it with the associated ticket ID and user ID.
     *
     * @param Asset $asset The newly created asset instance.
     * @param array $data Additional data related to the asset, such
     *                    as serial number and ticket number.
     * @param string|null $createdBy The user who is creating the asset.
     *
     * @return array The Jira description, ticket ID, and user ID.
     */
    private function getTicketComments($asset, $data, $createdBy = '')
    {
        $user = User::select('id', 'first_name', 'last_name', 'email')->find(Auth::id());

        $jiraDescription = __('jira.Created', [
            'assetname' => $data['serial_no'],
            'assetid' => $asset->id,
            'username' => $user?->userName,
            'useremail' => $user?->email,
            'created_by' => $createdBy ?: ''
        ]);

        return [
            'description' => $jiraDescription,
            'ticketId'   => $data['ticket_no'] ?? null,
            'userId' => $user?->id,
        ];
    }
}
