<?php

namespace App\Components;

use App\Facades\PrintQueue;
use App\Interface\DocumentOwner;
use App\Interface\HistoryOwner;
use App\Jobs\Synapse\ExtractEvidenceFromDocumentJob;
use App\Models\Document\Document;
use App\Models\Document\DocumentType;
use App\Models\Dsar\DsarEvidenceItem;
use App\Models\Solicitor\Solicitor;
use App\Models\Synapse\SynapseRun;
use App\Services\HistoryService;
use App\Synapse\Agents\DocumentDataExtractorAgent;
use App\Synapse\Agents\DocumentReviewerAgent;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Laravel\Ai\Files;
use Livewire\Component;
use Livewire\WithFileUploads;
use Livewire\WithPagination;
use Ramsey\Uuid\Uuid;
use App\Services\WipService;
use App\Trait\SupportsWip;

class DocumentManager extends Component {
    use AuthorizesRequests, WithFileUploads, WithPagination, SupportsWip;

    public DocumentOwner $owner;

    public bool $openAD = false;

    public $document_type_id;
    public $display_name;
    public $doc;

    public $filterType = '';

    // Synapse document reviewer
    public bool    $synapseOpen    = false;
    public bool    $synapseRunning = false;
    public bool    $synapseRan     = false;
    public ?string $synapseError   = null;
    public ?int    $synapseDocId   = null;
    public ?array  $synapseResult  = null;

    // Synapse data extractor
    public bool    $synapseExtracting    = false;
    public bool    $synapseExtracted     = false;
    public ?string $synapseExtractError  = null;
    public ?array  $synapseExtractResult = null;

    // Synapse evidence extractor
    public string  $synapseTab            = 'review';
    public string  $synapseEvidenceStatus = '';
    public int     $synapseEvidenceCount  = 0;
    public array   $synapseEvidenceItems  = [];

    public function openSynapse(int $id, string $tab = 'review'): void
    {
        $this->synapseDocId   = $id;
        $this->synapseTab     = $tab;
        $this->synapseOpen    = true;
        $this->synapseRunning = false;
        $this->synapseError   = null;

        $existing = SynapseRun::where('agent_type', 'document_reviewer')
            ->where('subject_type', Document::class)
            ->where('subject_id', $id)
            ->whereNotNull('result')
            ->latest()
            ->first();

        $this->synapseResult = $existing?->result;
        $this->synapseRan    = $existing !== null;

        $existingExtract = SynapseRun::where('agent_type', 'document_extractor')
            ->where('subject_type', Document::class)
            ->where('subject_id', $id)
            ->whereNotNull('result')
            ->latest()
            ->first();

        $this->synapseExtractResult = $existingExtract?->result;
        $this->synapseExtracted     = $existingExtract !== null;
        $this->synapseExtractError  = null;

        $this->loadEvidenceState($id);
    }

    private function loadEvidenceState(int $id): void
    {
        $doc = Document::find($id);
        if (!$doc) return;

        $this->synapseEvidenceStatus = $doc->evidence_status ?? '';

        $items = DsarEvidenceItem::where('document_id', $id)
            ->orderBy('page')
            ->orderBy('id')
            ->get();

        $this->synapseEvidenceItems = $items->map(fn($i) => [
            'id'           => $i->id,
            'page'         => $i->page,
            'quote'        => $i->quote,
            'category'     => $i->category,
            'severity'     => $i->severity,
            'comment'      => $i->comment,
            'source'       => $i->source,
            'confirmed_at' => $i->confirmed_at?->toDateTimeString(),
            'dismissed_at' => $i->dismissed_at?->toDateTimeString(),
            'human_notes'  => $i->human_notes,
        ])->all();

        $this->synapseEvidenceCount = $items->whereNull('dismissed_at')->count();
    }

    public function extractEvidence(): void
    {
        abort_if(!auth()->user()->can('synapse.use'), 403);

        $document = Document::find($this->synapseDocId);
        abort_if(!$document, 404);

        if ($document->evidence_status === 'processing') return;

        // Do NOT write 'processing' to the DB here — the job's atomic claim does that.
        // Only update the Livewire property so the UI reflects the queued state immediately.
        $this->synapseEvidenceStatus = 'processing';
        $this->synapseEvidenceItems  = [];
        $this->synapseEvidenceCount  = 0;

        ExtractEvidenceFromDocumentJob::dispatch($document->id);
    }

    public function rescanEvidence(): void
    {
        abort_if(!auth()->user()->can('synapse.use'), 403);

        $document = Document::find($this->synapseDocId);
        abort_if(!$document, 404);

        if ($document->evidence_status === 'processing') return;

        // Remove everything not explicitly confirmed — confirmed items survive the rescan.
        DsarEvidenceItem::where('document_id', $this->synapseDocId)
            ->whereNull('confirmed_at')
            ->delete();

        // Reset status to null so the job's atomic claim can pick it up.
        // Do NOT set to 'processing' here — the job owns that transition.
        $document->update(['evidence_status' => null]);

        $this->synapseEvidenceStatus = 'processing';
        $this->synapseEvidenceItems  = [];
        $this->synapseEvidenceCount  = 0;

        ExtractEvidenceFromDocumentJob::dispatch($document->id);
    }

    public function refreshEvidenceStatus(): void
    {
        if ($this->synapseDocId) {
            $this->loadEvidenceState($this->synapseDocId);
        }
    }

    public function confirmEvidenceItem(int $id): void
    {
        abort_if(!auth()->user()->can('synapse.use'), 403);
        DsarEvidenceItem::where('id', $id)
            ->where('document_id', $this->synapseDocId)
            ->update(['confirmed_at' => now(), 'dismissed_at' => null]);
        $this->loadEvidenceState($this->synapseDocId);
    }

    public function dismissEvidenceItem(int $id): void
    {
        abort_if(!auth()->user()->can('synapse.use'), 403);
        DsarEvidenceItem::where('id', $id)
            ->where('document_id', $this->synapseDocId)
            ->update(['dismissed_at' => now(), 'confirmed_at' => null]);
        $this->loadEvidenceState($this->synapseDocId);
    }

    public function closeSynapse(): void
    {
        $this->synapseOpen = false;
    }

    public function runSynapseReview(): void
    {
        abort_if(!auth()->user()->can('synapse.use'), 403);

        $document = Document::with('type')->find($this->synapseDocId);
        abort_if(!$document, 404);

        $this->synapseError   = null;
        $this->synapseRunning = true;
        $this->synapseRan     = false;
        $this->synapseResult  = null;

        try {
            $storagePath = $document->owner->getDocumentStoragePath() . $document->file_name;

            $attachment = $document->file_type === 'application/pdf'
                ? Files\Document::fromStorage($storagePath, disk: 'public')
                : Files\Image::fromStorage($storagePath, disk: 'public');

            $prompt = "Document name: " . $document->display_name . "\n"
                . "Document type: " . ($document->type?->display_name ?? 'Unknown') . "\n"
                . "File type: " . $document->file_type;

            $response = (new DocumentReviewerAgent)->prompt($prompt, attachments: [$attachment]);
            $result   = $response->structured;

            $this->synapseResult  = $result;
            $this->synapseRan     = true;
            $this->synapseRunning = false;

            if (config('synapse.record_runs', true)) {
                SynapseRun::create([
                    'solicitor_id' => app(Solicitor::class)?->id,
                    'user_id'      => auth()->id(),
                    'agent_type'   => 'document_reviewer',
                    'subject_type' => Document::class,
                    'subject_id'   => $document->id,
                    'status'       => 'completed',
                    'result'       => $result,
                ]);
            }
        } catch (\Throwable $e) {
            $this->synapseError   = 'Synapse encountered an error: ' . $e->getMessage();
            $this->synapseRunning = false;
            report($e);
        }
    }

    public function runSynapseExtract(): void
    {
        abort_if(!auth()->user()->can('synapse.use'), 403);

        $document = Document::with('type')->find($this->synapseDocId);
        abort_if(!$document, 404);

        $this->synapseExtractError  = null;
        $this->synapseExtracting    = true;
        $this->synapseExtracted     = false;
        $this->synapseExtractResult = null;

        try {
            $storagePath = $document->owner->getDocumentStoragePath() . $document->file_name;

            $attachment = $document->file_type === 'application/pdf'
                ? Files\Document::fromStorage($storagePath, disk: 'public')
                : Files\Image::fromStorage($storagePath, disk: 'public');

            $prompt = "Document name: " . $document->display_name . "\n"
                . "Document type hint: " . ($document->type?->display_name ?? 'Unknown');

            $response = (new DocumentDataExtractorAgent)->prompt($prompt, attachments: [$attachment]);
            $result   = $response->structured;

            $this->synapseExtractResult = $result;
            $this->synapseExtracted     = true;
            $this->synapseExtracting    = false;

            if (config('synapse.record_runs', true)) {
                SynapseRun::create([
                    'solicitor_id' => app(Solicitor::class)?->id,
                    'user_id'      => auth()->id(),
                    'agent_type'   => 'document_extractor',
                    'subject_type' => Document::class,
                    'subject_id'   => $document->id,
                    'status'       => 'completed',
                    'result'       => $result,
                ]);
            }
        } catch (\Throwable $e) {
            $this->synapseExtractError = 'Synapse encountered an error: ' . $e->getMessage();
            $this->synapseExtracting   = false;
            report($e);
        }
    }

    public function openAddDoc() {
        $this->openAD = true;
    }

    public function closeAddDoc() {
        $this->openAD = false;
    }

    public function deleteDocument(HistoryService $history, $id) {
        $this->authorize('documents.delete');

        $document = Document::find($id);

        if ($document) {
            $name = $document->display_name;
            $document->delete();

            $history->add($this->resolveHistoryOwner(), "Document deleted: {$name}", [
                'document_id' => $id,
                'display_name' => $name,
            ]);
        }
    }

    public function save(HistoryService $history, WipService $wipService) {
        $this->authorize('documents.upload');

        $validated = $this->validate([
            'display_name' => 'required|string|max:255',
            'doc' => 'required|file|mimes:pdf,doc,docx,jpg,png,mp3,eml,zip,xlsx,xls',
            'document_type_id' => 'required|integer|exists:document_types,id'
        ]);

        $originalName = $this->doc->getClientOriginalName();

        $path = $this->doc->storeAs(
            path: $this->owner->getDocumentStoragePath(),
            name: $originalName,
            options: 'public'
        );

        $document = $this->owner->documents()->create([
            'display_name' => $validated['display_name'],
            'file_name' => $originalName,
            'file_type' => $this->doc->getMimeType(),
            'file_size' => $this->doc->getSize(),
            'type_id' => $validated['document_type_id'],
            'user_id' => auth()->id()
        ]);

        $wipService->create([
            'client_id' => $this->resolveWipClientId(),
            'claim_id' => $this->resolveWipClaimId(),
            'activity_type' => 'document',
            'seconds' => 360,
            'units' => 1,
            'hours' => 0.10,
            'description' => 'Uploaded document: ' . $validated['display_name'],
            'started_at' => now(),
            'ended_at' => now(),
            'is_manual' => false,
        ], $document);

        if($validated['document_type_id'] == 4) {
            $this->owner?->claim?->keyDates()->syncWithoutDetaching([
                23 => ['date' => date('Y-m-d')],
                24 => ['date' => date('Y-m-d', strtotime('+14 day'))],
            ]);
        }

        $history->add($this->resolveHistoryOwner(), "Document uploaded: {$validated['display_name']}", [
            'document_id' => $document->id,
            'file_name' => $originalName,
            'mime_type' => $this->doc->getMimeType(),
        ]);

        $this->reset('doc', 'display_name', 'document_type_id');

        $this->closeAddDoc();
    }

    private function resolveHistoryOwner(): HistoryOwner {
        if ($this->owner instanceof \App\Models\Claim\Claim) {
            return $this->owner->client;
        }
        if ($this->owner instanceof \App\Models\Dsar\Dsar) {
            return $this->owner->claim->client;
        }
        return $this->owner;
    }

    public function print($id) {
        $this->authorize('documents.print');

        PrintQueue::add(Document::find($id));
        $this->dispatch('toast', message: "Sent document to print queue.", type: 'success');
    }

    public function render() {
        $query = $this->owner->documents();

        if ($this->filterType) {
            $query->where('type_id', $this->filterType);
        }

        $docs   = $query->latest()->paginate(10);
        $docIds = $docs->pluck('id');

        $latestRuns = SynapseRun::where('agent_type', 'document_reviewer')
            ->where('subject_type', Document::class)
            ->whereIn('subject_id', $docIds)
            ->whereNotNull('result')
            ->latest()
            ->get()
            ->unique('subject_id')
            ->keyBy('subject_id');

        // Evidence item counts per document (active/confirmed only — not dismissed)
        $evidenceCounts = DsarEvidenceItem::whereIn('document_id', $docIds)
            ->whereNull('dismissed_at')
            ->selectRaw('document_id, count(*) as count')
            ->groupBy('document_id')
            ->pluck('count', 'document_id');

        return view('components.document-manager', [
            'docs'           => $docs,
            'types'          => DocumentType::all(),
            'docTypes'       => DocumentType::pluck('display_name', 'id')->toArray(),
            'latestRuns'     => $latestRuns,
            'evidenceCounts' => $evidenceCounts,
        ]);
    }
}