<?php

namespace App\Components\Template;

use App\Enum\Contact\MessageChannel;
use App\Enum\Contact\TemplateTriggerType;
use App\Models\Claim\Claim;
use App\Models\Claim\Status;
use App\Models\Client\Client;
use App\Models\Contact\ContactCategory;
use App\Models\Contact\ContactTemplate;
use App\Models\Contact\ContactTemplateTrigger;
use App\Models\Contact\Template;
use App\Models\Document\DocumentType;
use App\Models\Solicitor\Solicitor;
use App\Models\Solicitor\SolicitorEmailSender;
use App\Models\Solicitor\SolicitorSmsSender;
use App\Models\Tag;
use App\Models\User\User;
use App\Services\Communications\Placeholders\PlaceholderRegistry;
use App\Services\Communications\Placeholders\TemplateRenderer;
use App\ValueObjects\Communications\PlaceholderContext;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Attributes\Url;
use Livewire\Component;

class Editor extends Component
{
    public ?ContactTemplate $template = null;

    use AuthorizesRequests;

    /* Core fields */
    public ?int $category_id = null;

    public ?string $name = null;

    public ?string $key = null;

    public string $channel = 'email';

    public ?string $subject = null;

    public ?string $body_html_template = null;

    public ?string $body_text_template = null;

    public ?int $default_sender_email_id = null;

    public ?int $default_sms_sender_id = null;

    public ?string $applies_to_type = null;

    public string $recipient_type = 'client';

    /** @var array<int, string> User IDs to send to when recipient_type === 'user'. */
    public array $recipientUserIds = [];

    public ?string $description = null;

    public bool $is_active = true;

    public bool $allow_manual_send = true;

    public bool $allow_automatic_send = false;

    public bool $include_signature = true;

    /* Preview context */
    #[Url]
    public ?int $previewClientId = null;

    public ?string $previewSubject = null;

    public ?string $previewBodyText = null;

    public ?string $previewBodyHtml = null;

    public array $previewPlaceholdersUsed = [];

    public array $previewUnknown = [];

    public ?int $smsLength = null;

    public ?int $smsSegments = null;

    public ?string $smsEncoding = null;

    /* Trigger editor state */
    public bool $showTriggerForm = false;

    public ?int $editingTriggerId = null;

    public array $triggerForm = [
        'trigger_type' => 'status_changed',
        'model_type' => null,
        'conditions' => '{}',
        'dedupe_window_minutes' => 60,
        'is_active' => true,
    ];

    public array $triggerConditionToStatusIds = [];

    public array $triggerConditionFromStatusIds = [];

    public array $triggerConditionTagIds = [];

    public bool $triggerShowAdvancedConditions = false;

    /** Document type IDs to attach when this template is sent. */
    public array $documentTypeIds = [];

    /** @var array<int, string> */
    public array $unknownAtSave = [];

    protected function rules(): array
    {
        return [
            'name' => 'required|min:2',
            'key' => 'nullable|string|max:255',
            'channel' => 'required|in:email,sms',
            'category_id' => 'nullable|exists:contact_categories,id',
            'subject' => 'nullable|string|max:512',
            'body_text_template' => 'nullable|string',
            'body_html_template' => 'nullable|string',
            'default_sender_email_id' => 'nullable|exists:solicitor_email_senders,id',
            'default_sms_sender_id' => 'nullable|exists:solicitor_sms_senders,id',
            'applies_to_type' => 'nullable|string|max:255',
            'recipient_type' => 'required|in:client,scheme,user,institution,case_worker',
            'recipientUserIds' => 'array',
            'recipientUserIds.*' => 'integer|exists:users,id',
            'description' => 'nullable|string|max:1000',
        ];
    }

    public function mount(?ContactTemplate $template = null): void
    {
        $this->authorize($template?->exists ? 'templates.edit' : 'templates.create');

        if ($template?->exists) {
            $this->template = $template;
            $this->category_id = $template->category_id;
            $this->name = $template->name;
            $this->key = $template->key;
            $this->channel = $template->channel?->value ?? 'email';
            $this->subject = $template->subject;
            $this->body_html_template = $template->body_html_template;
            $this->body_text_template = $template->body_text_template;
            $this->default_sender_email_id = $template->default_sender_email_id;
            $this->default_sms_sender_id = $template->default_sms_sender_id;
            // Normalise legacy Claim-only value to Client since they are now merged (1:1).
            $this->applies_to_type = $template->applies_to_type === Claim::class
                ? Client::class
                : $template->applies_to_type;
            $this->recipient_type = $template->recipient_type ?? 'client';
            $this->recipientUserIds = array_map('strval', $template->config['recipient_user_ids'] ?? []);
            $this->description = $template->description;
            $this->is_active = (bool) $template->is_active;
            $this->allow_manual_send = (bool) $template->allow_manual_send;
            $this->allow_automatic_send = (bool) $template->allow_automatic_send;
            $this->include_signature = (bool) ($template->config['include_signature'] ?? true);
            $this->documentTypeIds = $template->documentTypes->pluck('id')->map(fn ($id) => (string) $id)->toArray();
        } else {
            $this->channel = request('channel', 'email');
            $this->body_html_template = "<p>Hi {{ client.first_name|there }},</p>\n<p>Your message here...</p>";
            $this->body_text_template = "Hi {{ client.first_name|there }},\n\nYour message here...";
        }
    }

    public function updated($property): void
    {
        if (in_array($property, [
            'channel', 'subject', 'body_html_template', 'body_text_template',
            'default_sender_email_id', 'default_sms_sender_id',
            'include_signature', 'previewClientId',
        ], true)) {
            $this->refreshPreview();
        }
    }

    public function save(TemplateRenderer $renderer)
    {
        $this->authorize($this->template?->exists ? 'templates.edit' : 'templates.create');
        $this->validate();

        $data = [
            'category_id' => $this->category_id,
            'name' => $this->name,
            'key' => $this->key,
            'channel' => MessageChannel::from($this->channel),
            'subject' => $this->channel === 'email' ? $this->subject : null,
            'body_html_template' => $this->channel === 'email' ? $this->body_html_template : null,
            'body_text_template' => $this->body_text_template,
            'default_sender_email_id' => $this->channel === 'email' ? $this->default_sender_email_id : null,
            'default_sms_sender_id' => $this->channel === 'sms' ? $this->default_sms_sender_id : null,
            'applies_to_type' => $this->applies_to_type ?: null,
            'recipient_type' => $this->recipient_type,
            'description' => $this->description,
            'is_active' => $this->is_active,
            'allow_manual_send' => $this->allow_manual_send,
            'allow_automatic_send' => $this->allow_automatic_send,
            'config' => [
                'include_signature' => $this->include_signature,
                'recipient_user_ids' => $this->recipient_type === 'user'
                    ? array_values(array_map('intval', $this->recipientUserIds))
                    : [],
            ],
        ];

        if ($this->template?->exists) {
            $this->template->update($data);
        } else {
            $this->template = ContactTemplate::create($data);
        }

        $this->template->documentTypes()->sync(
            array_map('intval', $this->documentTypeIds)
        );

        $this->unknownAtSave = $renderer->validate($this->template);

        session()->flash('template_saved', 'Template saved.');

        if (empty($this->unknownAtSave)) {
            return redirect()->route('templates.edit', $this->template);
        }
    }

    /* ---------- Placeholder insertion ---------- */

    public function insertPlaceholder(string $token, string $target): void
    {
        $snippet = '{{ '.$token.' }}';
        match ($target) {
            'subject' => $this->subject = ($this->subject ?? '').$snippet,
            'html' => $this->body_html_template = ($this->body_html_template ?? '').$snippet,
            'text' => $this->body_text_template = ($this->body_text_template ?? '').$snippet,
            default => null,
        };
        $this->refreshPreview();
    }

    /* ---------- Preview ---------- */

    public function refreshPreview(): void
    {
        if (! $this->template && ! ($this->name && $this->channel)) {
            return;
        }

        // Build an ephemeral template instance for preview so unsaved edits render.
        $preview = $this->template?->exists ? $this->template->replicate() : new ContactTemplate;
        $preview->name = $this->name ?? 'preview';
        $preview->channel = MessageChannel::from($this->channel);
        $preview->subject = $this->subject;
        $preview->body_html_template = $this->body_html_template;
        $preview->body_text_template = $this->body_text_template;
        $preview->config = ['include_signature' => $this->include_signature];

        $sampleClient = $this->resolveSampleClient();
        $sender = $this->resolvePreviewSender();

        $ctx = PlaceholderContext::fromMessageable(
            messageable: $sampleClient,
            solicitor: app(Solicitor::class),
            user: auth()->user(),
            sender: $sender,
        );

        $renderer = app(TemplateRenderer::class);
        $rendered = $renderer->render($preview, $ctx, strict: false);

        $this->previewSubject = $rendered->subject;
        $this->previewBodyText = $rendered->bodyText;
        $this->previewBodyHtml = $rendered->bodyHtml;
        $this->previewPlaceholdersUsed = $rendered->placeholdersUsed;
        $this->previewUnknown = $rendered->unknownPlaceholders;

        if ($preview->isSms()) {
            $seg = $renderer->previewSegments($rendered, MessageChannel::SMS);
            $this->smsLength = $seg['length'];
            $this->smsSegments = $seg['segments'];
            $this->smsEncoding = $seg['encoding'];
        } else {
            $this->smsLength = null;
            $this->smsSegments = null;
            $this->smsEncoding = null;
        }
    }

    private function resolveSampleClient(): ?Client
    {
        if ($this->previewClientId) {
            return Client::with('claim.status')->find($this->previewClientId);
        }

        return Client::with('claim.status')->orderBy('id')->first();
    }

    private function resolvePreviewSender()
    {
        if ($this->channel === 'email') {
            if ($this->default_sender_email_id) {
                return SolicitorEmailSender::find($this->default_sender_email_id);
            }

            return app(Solicitor::class)?->emailSenderFor('general');
        }

        if ($this->default_sms_sender_id) {
            return SolicitorSmsSender::find($this->default_sms_sender_id);
        }

        return SolicitorSmsSender::query()->active()->orderByDesc('is_default')->first();
    }

    /* ---------- Trigger management ---------- */

    public function openTriggerForm(?int $id = null): void
    {
        $this->authorize('communications.triggers.manage');

        $this->showTriggerForm = true;
        $this->editingTriggerId = $id;
        $this->triggerShowAdvancedConditions = false;

        if ($id) {
            $t = ContactTemplateTrigger::findOrFail($id);
            $this->triggerForm = [
                'trigger_type' => $t->trigger_type->value,
                'model_type' => $t->model_type,
                'conditions' => json_encode($t->conditions ?? new \stdClass, JSON_PRETTY_PRINT),
                'dedupe_window_minutes' => $t->dedupe_window_minutes,
                'is_active' => (bool) $t->is_active,
            ];
            $this->loadTriggerConditionsIntoUi($t->conditions ?? []);
        } else {
            $this->triggerForm = [
                'trigger_type' => 'status_changed',
                'model_type' => $this->applies_to_type,
                'conditions' => '{}',
                'dedupe_window_minutes' => 60,
                'is_active' => true,
            ];
            $this->triggerConditionToStatusIds = [];
            $this->triggerConditionFromStatusIds = [];
            $this->triggerConditionTagIds = [];
        }
    }

    public function cancelTrigger(): void
    {
        $this->showTriggerForm = false;
        $this->editingTriggerId = null;
    }

    public function updatedTriggerFormTriggerType(): void
    {
        $this->triggerConditionToStatusIds = [];
        $this->triggerConditionFromStatusIds = [];
        $this->triggerConditionTagIds = [];
        $this->triggerForm['conditions'] = '{}';
        $this->triggerShowAdvancedConditions = false;
    }

    public function toggleAdvancedTriggerConditions(): void
    {
        $this->triggerShowAdvancedConditions = ! $this->triggerShowAdvancedConditions;
        if ($this->triggerShowAdvancedConditions) {
            $this->triggerForm['conditions'] = json_encode($this->buildTriggerConditions(), JSON_PRETTY_PRINT);
        }
    }

    public function saveTrigger(): void
    {
        $this->authorize('communications.triggers.manage');

        if (! $this->template?->exists) {
            session()->flash('template_error', 'Save the template before adding triggers.');

            return;
        }

        if ($this->triggerShowAdvancedConditions) {
            $decoded = json_decode($this->triggerForm['conditions'] ?: '{}', true);
            if (json_last_error() !== JSON_ERROR_NONE) {
                $this->addError('triggerForm.conditions', 'Conditions must be valid JSON.');

                return;
            }
            $conditions = $decoded ?: null;
        } else {
            $conditions = $this->buildTriggerConditions() ?: null;
        }

        $payload = [
            'contact_template_id' => $this->template->id,
            'trigger_type' => TemplateTriggerType::from($this->triggerForm['trigger_type']),
            'model_type' => $this->triggerForm['model_type'] ?: null,
            'conditions' => $conditions,
            'dedupe_window_minutes' => (int) $this->triggerForm['dedupe_window_minutes'],
            'is_active' => (bool) $this->triggerForm['is_active'],
        ];

        if ($this->editingTriggerId) {
            ContactTemplateTrigger::findOrFail($this->editingTriggerId)->update($payload);
        } else {
            ContactTemplateTrigger::create($payload);
        }

        $this->showTriggerForm = false;
        $this->editingTriggerId = null;
        $this->triggerConditionToStatusIds = [];
        $this->triggerConditionFromStatusIds = [];
        $this->triggerConditionTagIds = [];
        $this->triggerShowAdvancedConditions = false;
    }

    private function buildTriggerConditions(): array
    {
        $type = $this->triggerForm['trigger_type'];
        $conditions = [];

        if ($type === TemplateTriggerType::STATUS_CHANGED->value) {
            if ($this->triggerConditionToStatusIds) {
                $conditions['to_status_id'] = array_map('intval', $this->triggerConditionToStatusIds);
            }
            if ($this->triggerConditionFromStatusIds) {
                $conditions['from_status_id'] = array_map('intval', $this->triggerConditionFromStatusIds);
            }
        } elseif (in_array($type, [TemplateTriggerType::TAG_ADDED->value, TemplateTriggerType::TAG_REMOVED->value])) {
            if ($this->triggerConditionTagIds) {
                $conditions['tag_id'] = array_map('intval', $this->triggerConditionTagIds);
            }
        }

        return $conditions;
    }

    private function loadTriggerConditionsIntoUi(array $conditions): void
    {
        $toStatus = $conditions['to_status_id'] ?? null;
        $this->triggerConditionToStatusIds = is_array($toStatus)
            ? array_map('intval', $toStatus)
            : ($toStatus ? [(int) $toStatus] : []);

        $fromStatus = $conditions['from_status_id'] ?? null;
        $this->triggerConditionFromStatusIds = is_array($fromStatus)
            ? array_map('intval', $fromStatus)
            : ($fromStatus ? [(int) $fromStatus] : []);

        $tagId = $conditions['tag_id'] ?? null;
        $this->triggerConditionTagIds = is_array($tagId)
            ? array_map('intval', $tagId)
            : ($tagId ? [(int) $tagId] : []);
    }

    public function deleteTrigger(int $id): void
    {
        $this->authorize('communications.triggers.manage');
        ContactTemplateTrigger::destroy($id);
    }

    /* ---------- Render ---------- */

    public function render()
    {
        // Ensure preview is fresh for initial render.
        if ($this->previewSubject === null && $this->previewBodyText === null && $this->previewBodyHtml === null) {
            try {
                $this->refreshPreview();
            } catch (\Throwable) {
                // Preview failure must never prevent render() from completing,
                // since render() runs after save() and a throw here would swallow the redirect.
            }
        }

        $placeholders = app(PlaceholderRegistry::class)
            ->availableFor($this->applies_to_type ?: null);

        $placeholdersByPrefix = collect($placeholders)
            ->groupBy(fn ($p) => explode('.', $p->token)[0] ?? 'other');

        $triggers = $this->template?->exists
            ? ContactTemplateTrigger::where('contact_template_id', $this->template->id)
                ->orderByDesc('id')->get()
            : collect();

        $previewClients = Client::query()
            ->orderBy('name_first')
            ->limit(30)
            ->get(['id', 'name_first', 'name_last']);

        return view('components.template.editor', [
            'categories' => ContactCategory::orderBy('name')->get(),
            'documentTypes' => DocumentType::orderBy('display_name')->get(),
            'solicitorUsers' => User::where('solicitor_id', app(Solicitor::class)?->id)->orWhereNull('solicitor_id')
                ->orderBy('name_first')->orderBy('name_last')
                ->get(['id', 'name_first', 'name_last', 'email_address']),
            'emailSenders' => SolicitorEmailSender::orderByDesc('is_default')->get(),
            'smsSenders' => SolicitorSmsSender::query()->active()->orderByDesc('is_default')->orderBy('label')->get(),
            'placeholdersByPrefix' => $placeholdersByPrefix,
            'triggerTypes' => TemplateTriggerType::cases(),
            'triggers' => $triggers,
            'previewClients' => $previewClients,
            'statuses' => Status::query()->orderBy('display_name')->get(['id', 'display_name']),
            'tags' => Tag::query()->orderBy('name')->get(['id', 'name']),
            'statusMap' => Status::query()->pluck('display_name', 'id'),
            'tagMap' => Tag::query()->pluck('name', 'id'),
            'availableModels' => [
                '' => 'Any record type',
                Client::class => 'Client / Claim',
            ],
        ]);
    }
}
