<?php

namespace App\Components\Contact;

use App\Jobs\Contact\NotifyCaseHandlersOfEmailJob;
use App\Jobs\Synapse\ClassifyInboundEmailJob;
use App\Models\Client\Client;
use App\Models\Contact\Hub\ContactMessage;
use App\Models\Contact\Hub\ContactMessageAttachment;
use App\Models\Solicitor\Solicitor;
use App\Models\Solicitor\SolicitorEmailSender;
use App\Trait\LogsInboxActivity;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Attributes\Url;
use Livewire\Component;
use Livewire\WithPagination;

class InboxIndex extends Component
{
    use AuthorizesRequests, WithPagination, LogsInboxActivity;

    #[Url]
    public ?int $senderId = null;

    #[Url]
    public ?int $messageId = null;

    #[Url]
    public string $search = '';

    /**
     * Classification filter.
     * '' = all | 'dsar_response' | 'dsar_matched' | 'dsar_unmatched' | 'generic' | 'unclassified'
     */
    #[Url(as: 'filter')]
    public string $classificationFilter = '';

    /** Manual "link to client" search panel state for the open email. */
    public bool $linkingClient = false;

    public string $clientSearch = '';

    public function mount(): void
    {
        $this->authorize('inbox.view');

        if (!$this->senderId) {
            $first = $this->senders()->first();
            $this->senderId = $first?->id;
        }
    }

    public function selectSender(int $id): void
    {
        $this->senderId = $id;
        $this->messageId = null;
        $this->search = '';
        $this->classificationFilter = '';
        $this->resetClientLinking();
        $this->resetPage();
    }

    public function setClassificationFilter(string $filter): void
    {
        $this->classificationFilter = $filter;
        $this->messageId = null;
        $this->resetPage();
    }

    public function openMessage(int $id): void
    {
        $this->messageId = $id;
        $this->resetClientLinking();

        $opened = ContactMessage::where('id', $id)
            ->whereNull('read_at')
            ->update(['read_at' => now()]);

        // Log the open only when it was actually unread — re-opening a read
        // email shouldn't spam the trail.
        if ($opened) {
            $this->recordInboxAction(ContactMessage::find($id), 'Inbox: email opened');
        }
    }

    public function markUnread(int $id): void
    {
        ContactMessage::where('id', $id)->update(['read_at' => null]);

        $this->recordInboxAction(ContactMessage::find($id), 'Inbox: email marked unread');

        if ($this->messageId === $id) {
            $this->messageId = null;
        }
    }

    public function updatedSearch(): void
    {
        $this->resetPage();
        $this->messageId = null;
    }

    public function updatedClassificationFilter(): void
    {
        $this->resetPage();
        $this->messageId = null;
    }

    public function reclassify(int $id): void
    {
        ClassifyInboundEmailJob::dispatch($id, force: true);

        $this->recordInboxAction(ContactMessage::find($id), 'Inbox: AI re-classification requested');
    }

    // ── Manual client linking ──────────────────────────────────────────────────

    public function toggleClientLinking(): void
    {
        $this->linkingClient = ! $this->linkingClient;
        $this->clientSearch = '';
    }

    /**
     * Manually link the open email to a client — the fallback for when the sender
     * wrote from an address that didn't auto-match a client at ingest. Also used
     * to correct a wrong auto-match (re-link to a different client).
     */
    public function linkToClient(int $clientId): void
    {
        if (! $this->messageId) {
            return;
        }

        $solicitorId = app(Solicitor::class)->id;

        $message = ContactMessage::where('id', $this->messageId)
            ->where('solicitor_id', $solicitorId)
            ->first();

        $client = Client::where('solicitor_id', $solicitorId)
            ->whereKey($clientId)
            ->first();

        if (! $message || ! $client) {
            return;
        }

        $message->update([
            'messageable_type' => $client->getMorphClass(),
            'messageable_id'   => $client->id,
        ]);

        // Refresh so recordInboxAction sees the new link and mirrors it onto the
        // client's timeline.
        $this->recordInboxAction(
            $message->refresh(),
            'Inbox: email linked to client ' . $client->fullName,
        );

        // Now that the email sits on a client file, notify the case handler(s)
        // the same way an auto-matched email does at ingest.
        NotifyCaseHandlersOfEmailJob::dispatch($message->id);

        $this->resetClientLinking();

        $this->dispatch('toast', message: 'Email linked to client.', type: 'success');
    }

    public function unlinkClient(): void
    {
        if (! $this->messageId) {
            return;
        }

        $message = ContactMessage::where('id', $this->messageId)
            ->where('solicitor_id', app(Solicitor::class)->id)
            ->where('messageable_type', Client::class)
            ->first();

        if (! $message) {
            return;
        }

        // Log before clearing so the action still mirrors onto the (soon-removed)
        // client's timeline.
        $this->recordInboxAction($message, 'Inbox: email unlinked from client');

        $message->update([
            'messageable_type' => null,
            'messageable_id'   => null,
        ]);

        $this->resetClientLinking();

        $this->dispatch('toast', message: 'Email unlinked from client.', type: 'success');
    }

    private function resetClientLinking(): void
    {
        $this->linkingClient = false;
        $this->clientSearch = '';
    }

    public function saveAttachmentNote(int $attachmentId, string $text): void
    {
        $attachment = ContactMessageAttachment::whereHas('message', function ($q) {
            $q->where('solicitor_id', app(Solicitor::class)->id);
        })->findOrFail($attachmentId);

        $attachment->update([
            'extracted_text'    => trim($text),
            'extraction_status' => 'manual',
        ]);

        $this->recordInboxAction($attachment->message, 'Inbox: attachment text edited (' . $attachment->filename . ')', [
            'attachment_id' => $attachment->id,
        ]);
    }

    public function senders()
    {
        $solicitorId = app(Solicitor::class)->id;

        return SolicitorEmailSender::where('solicitor_id', $solicitorId)
            ->withCount([
                'inboundMessages as unread_count' => fn ($q) => $q->whereNull('read_at'),
            ])
            ->orderByDesc('is_default')
            ->orderBy('email_address')
            ->get();
    }

    public function render()
    {
        $senders = $this->senders();

        $messages = $this->senderId
            ? ContactMessage::inbound()
                ->where('sender_email_id', $this->senderId)
                ->withCount('attachments')
                ->when($this->search, fn ($q) => $q->where(function ($q) {
                    $q->where('subject', 'like', "%{$this->search}%")
                      ->orWhere('from_email', 'like', "%{$this->search}%")
                      ->orWhere('body_text', 'like', "%{$this->search}%");
                }))
                ->when($this->classificationFilter, function ($q) {
                    match ($this->classificationFilter) {
                        'dsar_response'  => $q->where('classification', 'dsar_response'),
                        'dsar_matched'   => $q->where('classification', 'dsar_response')->whereNotNull('messageable_type'),
                        'dsar_unmatched' => $q->where('classification', 'dsar_response')->whereNull('messageable_type'),
                        'generic'        => $q->where('classification', 'generic'),
                        'unclassified'   => $q->whereNull('classification'),
                        default          => null,
                    };
                })
                ->orderByDesc('created_at')
                ->paginate(25)
            : collect();

        $openMessage = $this->messageId
            ? ContactMessage::with([
                'attachments',
                'messageable.claim.client',
              ])->find($this->messageId)
            : null;

        $clientResults = collect();
        if ($openMessage && $this->linkingClient && strlen(trim($this->clientSearch)) >= 2) {
            $term = trim($this->clientSearch);

            $clientResults = Client::where('solicitor_id', app(Solicitor::class)->id)
                ->where(function ($q) use ($term) {
                    $q->where('name_first', 'like', "%{$term}%")
                      ->orWhere('name_last', 'like', "%{$term}%")
                      ->orWhere('email_address', 'like', "%{$term}%")
                      ->orWhere('reference', 'like', "%{$term}%");
                })
                ->orderBy('name_last')
                ->limit(20)
                ->get();
        }

        return view('components.contact.inbox-index', [
            'senders'       => $senders,
            'messages'      => $messages,
            'openMessage'   => $openMessage,
            'clientResults' => $clientResults,
        ]);
    }
}
