<?php

namespace App\Components\Client;

use App\Actions\Communications\EvaluateContactTriggersAction;
use App\Enum\Contact\TemplateTriggerType;
use App\Enum\Esign\EsignStatus;
use App\Models\Client\Client;
use App\Models\Contract;
use App\Models\ContractRequest;
use App\Services\Esign\ContractPreviewService;
use App\Services\EsignService;
use App\Services\HistoryService;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Component;

class Contracts extends Component {
    use AuthorizesRequests;

    public $contracts;
    public array $selectedContractIds = [];
    public array $previewsToRender = [];

    /** Fintrixor drafts created for preview, awaiting "Confirm & Send". */
    public array $pendingDocuments = [];

    /** The Fintrixor draft request (envelope) backing the pending documents. */
    public array $pendingRequest = [];

    public Client $client;

    public function mount(): void {
        $this->contracts = Contract::all();
    }

    /**
     * Pre-esign checklist. Returns a list of human-readable blockers.
     * An empty array means the application is ready to send.
     */
    public function getEsignBlockersProperty(): array {
        $blockers = [];
        $claim    = $this->client->claim;

        if (!$claim) {
            $blockers[] = 'No claim has been initialised for this client.';
            return $blockers;
        }

        if (!in_array($claim->redress_type, ['FIXED', 'INDIVIDUAL'], true)) {
            $blockers[] = 'Redress type must be set to Fixed or Individual.';
        }

        return $blockers;
    }

    /**
     * Build the preview: create ONE Fintrixor DRAFT request (auto_send=false)
     * bundling every selected, template-mapped contract, then show each contract's
     * real draft.pdf. Unmapped contracts (Part-1/3, manual) are skipped here and
     * handled separately.
     */
    public function processSignature(EsignService $esign, ContractPreviewService $previews): void {
        $this->authorize('claims.request.send');

        if (empty($this->selectedContractIds)) {
            $this->dispatch('toast', type: 'error', message: 'Please select at least one contract.');
            return;
        }

        if ($this->esignBlockers) {
            $this->dispatch('toast',
                type: 'error',
                message: 'Application is not ready: ' . implode(' ', $this->esignBlockers),
            );
            return;
        }

        $this->resetPreviewState();

        // One draft request (envelope) for all the selected, mapped contracts.
        $contracts    = Contract::whereIn('id', $this->selectedContractIds)->get();
        $draftRequest = $esign->createDraftRequest($contracts->all(), $this->client);

        if (!$draftRequest) {
            $this->dispatch('toast', type: 'error', message: 'Nothing could be previewed for the selected contract(s).');
            return;
        }

        $this->pendingRequest   = ['request_id' => $draftRequest['request_id']];
        $this->pendingDocuments = $draftRequest['drafts'];

        foreach ($draftRequest['drafts'] as $draft) {
            if ($draft['draft_url'] && $pdf = $esign->fetchDraftBase64($draft['draft_url'])) {
                // Key by the unique Fintrixor document id: one annex contract can
                // yield several drafts that would otherwise share a contract_id.
                $this->previewsToRender[] = [
                    'id'       => $draft['document_id'] ?? $draft['contract_id'],
                    'name'     => $draft['name'],
                    'pdf_data' => $pdf,
                ];
            }
        }

        if (!empty($this->previewsToRender)) {
            $this->dispatch('show-preview-modal', previews: $this->previewsToRender);
        } else {
            $this->dispatch('toast', type: 'error', message: 'Nothing could be previewed for the selected contract(s).');
        }
    }

    /**
     * Confirm: issue ONE signing link for the whole draft request, persist each
     * contract as an EsignDocument sharing that single link, and email the client
     * the one link.
     */
    public function confirmAndSend(EsignService $esign): void {
        $this->authorize('claims.request.send');

        if ($this->esignBlockers) {
            $this->dispatch('toast',
                type: 'error',
                message: 'Application is not ready: ' . implode(' ', $this->esignBlockers),
            );
            $this->dispatch('close-preview-modal');
            return;
        }

        // Only local/manual previews — nothing to send through Fintrixor.
        if (empty($this->pendingDocuments) || empty($this->pendingRequest['request_id'])) {
            $this->dispatch('toast', type: 'info', message: 'None of the previewed contracts are configured for e-signature — handle them manually.');
            $this->resetPreviewState();
            $this->dispatch('close-preview-modal');
            return;
        }

        $solicitor       = $this->client->solicitor;
        $providerRequest = $this->pendingRequest['request_id'];

        // Issue ONE signing link for the whole request (covers every contract in it).
        $response     = ($solicitor && $providerRequest)
            ? $esign->issueSendRequest($solicitor, $providerRequest)
            : null;
        $signingUrl   = $response['signing_url'] ?? null;
        $providerDocs = collect($response['documents'] ?? [])->keyBy('id');

        $request = ContractRequest::create([
            'client_id'    => $this->client->id,
            'solicitor_id' => $this->client->solicitor_id,
            'user_id'      => auth()->id(),
            'contract_ids' => $this->selectedContractIds,
            'status'       => EsignStatus::DRAFT,
            'link'         => $signingUrl,
            'full_data'    => ['provider_request_id' => $providerRequest, 'send_response' => $response],
        ]);

        $sent   = 0;
        $failed = [];

        foreach ($this->pendingDocuments as $pending) {
            $document = $request->esignDocuments()->create([
                'solicitor_id'         => $request->solicitor_id,
                'contract_id'          => $pending['contract_id'],
                'provider_document_id' => $pending['document_id'],
                'draft_url'            => $pending['draft_url'],
                // Every contract in the request shares the one request-level link.
                'signing_url'          => $signingUrl,
                'recipient_name'       => $this->client->fullName,
                'recipient_email'      => (string) $this->client->email_address,
                'status'               => EsignStatus::DRAFT,
                // Annex documents carry the care setting they were generated for.
                'meta'                 => isset($pending['care_setting_id']) && $pending['care_setting_id'] !== null
                    ? ['care_setting_id' => $pending['care_setting_id']]
                    : null,
            ]);

            if ($response && $signingUrl) {
                $providerDoc = $providerDocs->get($pending['document_id'])
                    ?? ['id' => $pending['document_id'], 'signing_url' => $signingUrl];
                $document->markSent($providerDoc);
                $sent++;
            } else {
                $document->markFailed('Failed to issue signing link.');
                $failed[] = $pending['name'];
            }
        }

        // Email the client the ONE signing link for all the contracts in this request
        // (fires any active "E-signature document sent" trigger template).
        if ($sent > 0 && $signingUrl) {
            $contractNames = collect($this->pendingDocuments)->pluck('name')->filter()->implode(', ');

            \Illuminate\Support\Facades\Log::info('eSign signing-link email: firing trigger (single request link)', [
                'contract_request_id' => $request->id,
                'documents'           => $sent,
                'has_signing_url'     => filled($signingUrl),
            ]);
            try {
                app(EvaluateContactTriggersAction::class)(
                    $this->client,
                    TemplateTriggerType::ESIGN_DOCUMENT_SENT,
                    ['contract_request_id' => $request->id, 'contract' => $contractNames],
                    ['esign.signing_url' => (string) $signingUrl, 'esign.contract_name' => $contractNames],
                );
            } catch (\Throwable $e) {
                report($e);
            }
        }

        $request->recomputeStatus();

        $message = "Sent {$sent} document(s) for e-signature."
            . ($failed ? ' Failed: ' . implode(', ', $failed) . '.' : '');
        app(HistoryService::class)->add($this->client, $message, ['contract_request_id' => $request->id]);

        $this->dispatch('toast', type: $sent > 0 ? 'success' : 'error', message: $message);

        $this->resetPreviewState();
        $this->selectedContractIds = [];
        $this->dispatch('esignRequestSent');
        $this->dispatch('close-preview-modal');
    }

    /**
     * Cancel the preview: discard the pending drafts. The Fintrixor drafts are
     * left to expire on their own (the API has no document-delete).
     */
    public function cancelDraft(): void {
        $this->resetPreviewState();
        $this->dispatch('close-preview-modal');
    }

    private function resetPreviewState(): void {
        $this->pendingDocuments = [];
        $this->pendingRequest   = [];
        $this->previewsToRender = [];
    }

    public function render() {
        return view('components.client.contracts');
    }
}
