<?php

namespace App\Services;

use App\Jobs\SendEmailTemplateJob;
use App\Models\Activity;
use App\Models\Client;
use App\Models\ClientCase;
use App\Models\Contract;
use App\Models\Decision;
use App\Models\Email;
use App\Models\EmailLog;
use App\Models\EmailLogStatus;
use App\Models\EmailTemplate;
use App\Models\MailProfile;

class AutoMailDispatcher
{
    public function __construct(public EmailTemplateRenderer $renderer) {}

    /**
     * Attempt to queue an auto mail for the given activity based on its decision/template.
     * Returns array with either ['queued' => true, 'log_id' => int] or ['skipped' => 'reason'].
     */
    public function maybeQueue(Activity $activity, bool $sendFlag = true, array $options = []): array
    {
        $decision = $activity->decision;
        if (! $sendFlag || ! $decision || ! $decision->auto_mail || ! $decision->email_template_id) {
            return ['skipped' => 'disabled'];
        }

        /** @var EmailTemplate|null $template */
        $template = EmailTemplate::query()->find($decision->email_template_id);
        if (! $template || ! $template->active) {
            return ['skipped' => 'no-template'];
        }

        // Resolve context
        $clientCase = $activity->clientCase; /** @var ClientCase|null $clientCase */
        $contract = $activity->contract; /** @var Contract|null $contract */
        $client = $clientCase?->client; /** @var Client|null $client */
        $person = $clientCase?->person; /** @var \App\Models\Person\Person|null $person */

        // Validate required entity types from template
        $required = (array) ($template->entity_types ?? []);
        if (in_array('client', $required, true) && ! $client) {
            return ['skipped' => 'missing-client'];
        }
        if (in_array('client_case', $required, true) && ! $clientCase) {
            return ['skipped' => 'missing-client-case'];
        }
        if (in_array('contract', $required, true) && ! $contract) {
            return ['skipped' => 'missing-contract'];
        }
        if (in_array('person', $required, true) && ! $person) {
            return ['skipped' => 'missing-person'];
        }

        // Resolve eligible recipients: client's person emails with receive_auto_mails = true
        $recipients = [];
        if ($client && $client->person) {
            $emails = Email::query()
                ->where('person_id', $client->person->id)
                ->where('is_active', true)
                ->where('receive_auto_mails', true)
                ->get(['value', 'preferences']);

            $recipients = $emails
                ->filter(function (Email $email) use ($decision): bool {
                    $decisionIds = $email->preferences['decision_ids'] ?? [];

                    // Empty list means "all decisions" — always receive
                    if (empty($decisionIds)) {
                        return true;
                    }

                    return in_array((int) $decision->id, array_map('intval', $decisionIds), true);
                })
                ->pluck('value')
                ->map(fn ($v) => strtolower(trim((string) $v)))
                ->filter(fn ($v) => filter_var($v, FILTER_VALIDATE_EMAIL))
                ->unique()
                ->values()
                ->all();
        }
        if (empty($recipients)) {
            return ['skipped' => 'no-recipients'];
        }

        // Ensure related names are available without extra queries
        $activity->loadMissing(['action', 'decision']);

        // Ensure account is available on contract (needed for contract.account.* tokens)
        if ($contract && ! $contract->relationLoaded('account')) {
            $contract->load('account');
        }

        // Resolve the sending profile once — used both for signature tokens and as the actual sender.
        // Prefer the profile explicitly requested via options, fall back to highest-priority active one.
        $mailProfile = isset($options['mail_profile_id'])
            ? MailProfile::query()->find($options['mail_profile_id'])
            : null;
        $mailProfile ??= MailProfile::query()
            ->where('active', true)
            ->where('auto_mailer', true)
            ->orderBy('priority')
            ->orderBy('id')
            ->first();
        $mailProfile ??= MailProfile::query()
            ->where('active', true)
            ->orderBy('priority')
            ->orderBy('id')
            ->first();

        // Render content
        $bodyText = isset($options['body_text']) ? (string) $options['body_text'] : '';
        $rendered = $this->renderer->render([
            'subject' => (string) $template->subject_template,
            'html' => (string) $template->html_template,
            'text' => (string) $template->text_template,
        ], [
            'client' => $client,
            'client_case' => $clientCase,
            'contract' => $contract,
            'person' => $person,
            'activity' => $activity,
            'extra' => [],
            'mail_profile' => $mailProfile,
            'body_text' => $bodyText,
        ]);

        // Create the log and body
        $log = new EmailLog;
        $log->fill([
            'uuid' => (string) \Str::uuid(),
            'template_id' => $template->id,
            'mail_profile_id' => $mailProfile?->id,
            'user_id' => auth()->id(),
            'to_email' => (string) ($recipients[0] ?? ''),
            'to_recipients' => $recipients,
            'subject' => (string) ($rendered['subject'] ?? $template->subject_template ?? ''),
            'body_html_hash' => $rendered['html'] ? hash('sha256', $rendered['html']) : null,
            'body_text_preview' => isset($rendered['text']) ? mb_strimwidth((string) $rendered['text'], 0, 4096) : null,
            'embed_mode' => 'base64',
            'status' => EmailLogStatus::Queued,
            'queued_at' => now(),
            'client_id' => $client?->id,
            'client_case_id' => $clientCase?->id,
            'contract_id' => $contract?->id,
        ]);
        // If multiple recipients, keep to_email null to avoid implying a single primary recipient
        if (count($recipients) > 1) {
            $log->to_email = null;
        }
        // Resolve and attach selected documents as attachments metadata (id, name, path, mime, size)
        $attachmentIds = collect($options['attachment_ids'] ?? [])->filter()->map(fn ($v) => (int) $v)->values();
        if ($attachmentIds->isNotEmpty()) {
            $docs = \App\Models\Document::query()
                ->whereIn('id', $attachmentIds)
                ->get(['id', 'disk', 'path', 'original_name', 'name', 'mime_type', 'size']);
            $log->attachments = $docs->map(function ($d) {
                return [
                    'id' => $d->id,
                    'disk' => $d->disk ?: 'public',
                    'path' => $d->path,
                    'name' => $d->original_name ?: ($d->name ?: basename($d->path)),
                    'mime' => $d->mime_type ?: 'application/octet-stream',
                    'size' => $d->size,
                ];
            })->values()->all();
        }

        $log->save();

        $log->body()->create([
            'body_html' => (string) ($rendered['html'] ?? ''),
            'body_text' => $bodyText !== '' ? $bodyText : (string) ($rendered['text'] ?? ''),
            'inline_css' => true,
        ]);

        dispatch(new SendEmailTemplateJob($log->id));

        return ['queued' => true, 'log_id' => $log->id];
    }
}
