<?php

namespace App\Components;

use App\Facades\PrintQueue;
use App\Interface\DocumentOwner;
use App\Interface\HistoryOwner;
use App\Models\Document\Document;
use App\Models\Document\DocumentType;
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;

    public function openSynapse(int $id): void
    {
        $this->synapseDocId   = $id;
        $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;
    }

    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);

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

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