<?php

namespace App\Services;

use App\Core\Services\Invoice\PdfService;
use App\Core\Services\Quotation\QuotationPdfService;
use App\Enums\ReminderType;
use App\Jobs\SendReminderEmailJob;
use App\Models\Customer;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\PurchaseOrder;
use App\Models\Quotation;
use App\Models\ReminderLog;
use App\Models\ReminderSetting;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

/**
 * Smart billing reminder automation — email channel today; structured for SMS/WhatsApp later.
 */
class ReminderService
{
    public function __construct(
        private EmailTemplateService $templates,
        private NotificationLogService $logs,
        private PdfService $invoicePdf,
        private QuotationPdfService $quotationPdf
    ) {}

    public function isEnabled(ReminderType $type, ?ReminderSetting $settings = null): bool
    {
        $s = $settings ?? ReminderSetting::current();

        return match ($type) {
            ReminderType::InvoiceCreated => $s->enable_invoice_created,
            ReminderType::InvoiceDueSoon => $s->enable_invoice_due_reminder,
            ReminderType::InvoiceOverdue => $s->enable_invoice_overdue_reminder,
            ReminderType::InvoicePaid => $s->enable_invoice_paid,
            ReminderType::PartialPaymentReceived => $s->enable_partial_payment,
            ReminderType::QuotationCreated => $s->enable_quotation_created,
            ReminderType::QuotationExpiry => $s->enable_quotation_expiry,
            ReminderType::QuotationAccepted => $s->enable_quotation_accepted,
            ReminderType::QuotationRejected => $s->enable_quotation_rejected,
            ReminderType::PurchaseOrderCreated => $s->enable_purchase_order_created,
            ReminderType::RecurringInvoiceGenerated => $s->enable_recurring_invoice_generated,
            ReminderType::PaymentReceipt => $s->enable_payment_receipt,
            ReminderType::ManualReminder => $s->enable_manual_reminders,
            ReminderType::FollowUpReminder => $s->enable_follow_up_reminder,
            ReminderType::CustomerWelcome => $s->enable_customer_welcome,
        };
    }

    /**
     * Prevent duplicate automated sends for the same entity + type on the same calendar day.
     */
    public function wasSentToday(
        string $reminderType,
        ?int $invoiceId = null,
        ?int $quotationId = null
    ): bool {
        if (in_array($reminderType, [ReminderType::ManualReminder->value, ReminderType::FollowUpReminder->value], true)) {
            return false;
        }

        $q = ReminderLog::query()
            ->where('reminder_type', $reminderType)
            ->where('status', 'sent')
            ->whereDate('created_at', now()->toDateString());

        if ($invoiceId) {
            $q->where('invoice_id', $invoiceId);
        }
        if ($quotationId) {
            $q->where('quotation_id', $quotationId);
        }

        return $q->exists();
    }

    public function validateEmail(?string $email): bool
    {
        if ($email === null || trim($email) === '') {
            return false;
        }

        return (bool) filter_var($email, FILTER_VALIDATE_EMAIL);
    }

    /**
     * @param  array<string, string>  $extraPlaceholders
     * @param  list<array{path?: string, content?: string, as?: string, mime?: string}>  $attachments
     */
    public function sendTemplated(
        ReminderType $type,
        string $recipientEmail,
        array $placeholders,
        ?int $customerId = null,
        ?int $invoiceId = null,
        ?int $quotationId = null,
        ?int $paymentId = null,
        ?int $purchaseOrderId = null,
        ?int $recurringInvoiceId = null,
        array $extraPlaceholders = [],
        array $attachments = [],
        bool $forceSkipDuplicateCheck = false
    ): bool {
        $settings = ReminderSetting::current();

        if (! $this->isEnabled($type, $settings)) {
            return false;
        }

        if (! $this->validateEmail($recipientEmail)) {
            Log::info('Reminder skipped: invalid email', ['type' => $type->value]);

            return false;
        }

        if (! $forceSkipDuplicateCheck && $this->wasSentToday($type->value, $invoiceId, $quotationId)) {
            return false;
        }

        $tpl = $this->templates->getTemplate($type->templateSlug());
        if (! $tpl) {
            Log::warning('Reminder skipped: missing template', ['slug' => $type->templateSlug()]);

            return false;
        }

        $merged = $this->templates->mergePlaceholders($placeholders, $extraPlaceholders);
        $subject = $this->templates->apply($tpl->subject, $merged);
        $body = $this->templates->apply($this->templates->sanitizeBody($tpl->body), $merged);
        $text = Str::limit(trim(strip_tags(str_replace(['<br>', '<br/>', '<br />'], "\n", $body))), 10000);

        $cc = [];
        if ($settings->send_copy_to_admin) {
            $admin = \App\Models\CompanySetting::getSettings()->company_email ?? null;
            if ($admin && $this->validateEmail($admin) && strcasecmp($admin, $recipientEmail) !== 0) {
                $cc[] = $admin;
            }
        }

        $binaryAttachments = $this->normalizeAttachmentsForJob($attachments);

        $log = $this->logs->createPending([
            'customer_id' => $customerId,
            'invoice_id' => $invoiceId,
            'quotation_id' => $quotationId,
            'payment_id' => $paymentId,
            'purchase_order_id' => $purchaseOrderId,
            'recurring_invoice_id' => $recurringInvoiceId,
            'reminder_type' => $type->value,
            'subject' => $subject,
            'recipient_email' => $recipientEmail,
            'created_by' => auth()->id(),
        ]);

        try {
            if ($settings->usesQueue()) {
                SendReminderEmailJob::dispatch(
                    $log->id,
                    $subject,
                    $body,
                    $text,
                    $binaryAttachments,
                    $cc
                );
            } else {
                SendReminderEmailJob::dispatchSync(
                    $log->id,
                    $subject,
                    $body,
                    $text,
                    $binaryAttachments,
                    $cc
                );
            }
        } catch (\Throwable $e) {
            $this->logs->markFailed($log, $e->getMessage());
            Log::error('Reminder dispatch failed: '.$e->getMessage());

            return false;
        }

        return true;
    }

    /**
     * @return list<array{content: string, as: string, mime: string}>
     */
    public function buildInvoicePdfAttachment(Invoice $invoice): array
    {
        return [[
            'content' => $this->invoicePdf->generate($invoice),
            'as' => 'invoice-'.$invoice->invoice_number.'.pdf',
            'mime' => 'application/pdf',
        ]];
    }

    /**
     * @return list<array{content: string, as: string, mime: string}>
     */
    public function buildQuotationPdfAttachment(Quotation $quotation): array
    {
        return [[
            'content' => $this->quotationPdf->generate($quotation),
            'as' => 'quotation-'.$quotation->quotation_number.'.pdf',
            'mime' => 'application/pdf',
        ]];
    }

    /**
     * @param  list<array{path?: string, content?: string, as?: string, mime?: string}>  $attachments
     * @return list<array{content: string, as: string, mime: string}>
     */
    private function normalizeAttachmentsForJob(array $attachments): array
    {
        $out = [];
        foreach ($attachments as $row) {
            if (! empty($row['content'])) {
                $out[] = [
                    'content' => $row['content'],
                    'as' => $row['as'] ?? 'attachment.pdf',
                    'mime' => $row['mime'] ?? 'application/pdf',
                ];

                continue;
            }
            if (! empty($row['path']) && is_file($row['path'])) {
                $out[] = [
                    'content' => (string) file_get_contents($row['path']),
                    'as' => $row['as'] ?? basename($row['path']),
                    'mime' => $row['mime'] ?? 'application/octet-stream',
                ];
                @unlink($row['path']);
            }
        }

        return $out;
    }

    public function sendInvoiceCreated(Invoice $invoice): void
    {
        try {
            $invoice->loadMissing('customer');
            if (! $invoice->customer?->email) {
                return;
            }
            if (in_array($invoice->status, ['draft', 'cancelled'], true)) {
                return;
            }

            $settings = ReminderSetting::current();
            $atts = $settings->attach_pdf_to_emails ? $this->buildInvoicePdfAttachment($invoice) : [];

            $this->sendTemplated(
                ReminderType::InvoiceCreated,
                $invoice->customer->email,
                $this->templates->invoicePlaceholders($invoice),
                $invoice->customer_id,
                $invoice->id,
                null,
                null,
                null,
                $invoice->recurring_invoice_id,
                [],
                $atts
            );
        } catch (\Throwable $e) {
            Log::warning('sendInvoiceCreated reminder: '.$e->getMessage());
        }
    }

    public function sendInvoiceDueSoon(Invoice $invoice): void
    {
        try {
            $invoice->loadMissing('customer');
            if (! $invoice->customer?->email) {
                return;
            }
            if ($invoice->isPaid() || $invoice->status === 'cancelled') {
                return;
            }
            $days = ReminderSetting::current()->due_reminder_days_before;
            if (! $invoice->due_date || ! $invoice->due_date->isSameDay(now()->addDays($days))) {
                return;
            }

            $this->sendTemplated(
                ReminderType::InvoiceDueSoon,
                $invoice->customer->email,
                $this->templates->invoicePlaceholders($invoice),
                $invoice->customer_id,
                $invoice->id
            );
        } catch (\Throwable $e) {
            Log::warning('sendInvoiceDueSoon: '.$e->getMessage());
        }
    }

    public function sendInvoiceOverdue(Invoice $invoice): void
    {
        try {
            $invoice->loadMissing('customer');
            if (! $invoice->customer?->email) {
                return;
            }
            if ($invoice->isPaid() || $invoice->status === 'cancelled') {
                return;
            }
            if (! $invoice->isOverdue()) {
                return;
            }

            $this->sendTemplated(
                ReminderType::InvoiceOverdue,
                $invoice->customer->email,
                $this->templates->invoicePlaceholders($invoice),
                $invoice->customer_id,
                $invoice->id
            );
        } catch (\Throwable $e) {
            Log::warning('sendInvoiceOverdue: '.$e->getMessage());
        }
    }

    public function sendInvoicePaid(Invoice $invoice): void
    {
        try {
            $invoice->loadMissing('customer');
            if (! $invoice->customer?->email) {
                return;
            }

            $this->sendTemplated(
                ReminderType::InvoicePaid,
                $invoice->customer->email,
                $this->templates->invoicePlaceholders($invoice),
                $invoice->customer_id,
                $invoice->id
            );
        } catch (\Throwable $e) {
            Log::warning('sendInvoicePaid: '.$e->getMessage());
        }
    }

    public function sendPartialPayment(Payment $payment): void
    {
        try {
            $payment->loadMissing(['invoice', 'customer']);
            $invoice = $payment->invoice;
            if (! $invoice || ! $payment->customer?->email) {
                return;
            }
            if ($invoice->isPaid()) {
                return;
            }

            $placeholders = $this->templates->paymentPlaceholders($payment);

            $this->sendTemplated(
                ReminderType::PartialPaymentReceived,
                $payment->customer->email,
                $placeholders,
                $payment->customer_id,
                $invoice->id,
                null,
                $payment->id
            );
        } catch (\Throwable $e) {
            Log::warning('sendPartialPayment: '.$e->getMessage());
        }
    }

    public function sendQuotationCreated(Quotation $quotation): void
    {
        try {
            $quotation->loadMissing('customer');
            if (! $quotation->customer?->email) {
                return;
            }
            if (! in_array($quotation->status, ['sent', 'viewed'], true)) {
                return;
            }

            $settings = ReminderSetting::current();
            $atts = $settings->attach_pdf_to_emails ? $this->buildQuotationPdfAttachment($quotation) : [];

            $this->sendTemplated(
                ReminderType::QuotationCreated,
                $quotation->customer->email,
                $this->templates->quotationPlaceholders($quotation),
                $quotation->customer_id,
                null,
                $quotation->id,
                null,
                null,
                null,
                [],
                $atts
            );
        } catch (\Throwable $e) {
            Log::warning('sendQuotationCreated: '.$e->getMessage());
        }
    }

    public function sendQuotationExpiry(Quotation $quotation): void
    {
        try {
            $quotation->loadMissing('customer');
            if (! $quotation->customer?->email || ! $quotation->expiry_date) {
                return;
            }
            if (in_array($quotation->status, ['converted', 'rejected', 'cancelled'], true)) {
                return;
            }

            $days = ReminderSetting::current()->quotation_expiry_days_before;
            if (! $quotation->expiry_date->isSameDay(now()->addDays($days))) {
                return;
            }

            $this->sendTemplated(
                ReminderType::QuotationExpiry,
                $quotation->customer->email,
                $this->templates->quotationPlaceholders($quotation),
                $quotation->customer_id,
                null,
                $quotation->id
            );
        } catch (\Throwable $e) {
            Log::warning('sendQuotationExpiry: '.$e->getMessage());
        }
    }

    public function sendQuotationAccepted(Quotation $quotation): void
    {
        try {
            $quotation->loadMissing('customer');
            if (! $quotation->customer?->email) {
                return;
            }

            $this->sendTemplated(
                ReminderType::QuotationAccepted,
                $quotation->customer->email,
                $this->templates->quotationPlaceholders($quotation),
                $quotation->customer_id,
                null,
                $quotation->id
            );
        } catch (\Throwable $e) {
            Log::warning('sendQuotationAccepted: '.$e->getMessage());
        }
    }

    public function sendQuotationRejected(Quotation $quotation): void
    {
        try {
            $quotation->loadMissing('customer');
            $to = $quotation->customer?->email;
            if (! $to || ! $this->validateEmail($to)) {
                $to = \App\Models\CompanySetting::getSettings()->company_email ?? null;
            }
            if (! $to || ! $this->validateEmail($to)) {
                return;
            }

            $this->sendTemplated(
                ReminderType::QuotationRejected,
                $to,
                $this->templates->quotationPlaceholders($quotation),
                $quotation->customer_id,
                null,
                $quotation->id
            );
        } catch (\Throwable $e) {
            Log::warning('sendQuotationRejected: '.$e->getMessage());
        }
    }

    public function sendPurchaseOrderCreated(PurchaseOrder $po): void
    {
        try {
            $po->loadMissing('supplier');
            $email = $po->supplier?->email ?? null;
            if (! $email) {
                return;
            }

            $c = \App\Models\CompanySetting::getSettings();
            $placeholders = array_merge($this->templates->basePlaceholders(), [
                'purchase_order_number' => $po->order_number,
                'supplier_name' => $po->supplier->name ?? '',
                'purchase_total' => (string) $po->total,
                'company_name' => $c->company_name ?? config('app.name'),
            ]);

            $this->sendTemplated(
                ReminderType::PurchaseOrderCreated,
                $email,
                $placeholders,
                null,
                null,
                null,
                null,
                $po->id,
            );
        } catch (\Throwable $e) {
            Log::warning('sendPurchaseOrderCreated: '.$e->getMessage());
        }
    }

    public function sendRecurringInvoiceGenerated(Invoice $invoice): void
    {
        try {
            $invoice->loadMissing('customer');
            if (! $invoice->customer?->email) {
                return;
            }

            $this->sendTemplated(
                ReminderType::RecurringInvoiceGenerated,
                $invoice->customer->email,
                $this->templates->invoicePlaceholders($invoice),
                $invoice->customer_id,
                $invoice->id,
                null,
                null,
                null,
                $invoice->recurring_invoice_id
            );
        } catch (\Throwable $e) {
            Log::warning('sendRecurringInvoiceGenerated: '.$e->getMessage());
        }
    }

    public function sendPaymentReceipt(Payment $payment): void
    {
        try {
            $payment->loadMissing(['customer', 'invoice']);
            if (! $payment->customer?->email) {
                return;
            }

            $this->sendTemplated(
                ReminderType::PaymentReceipt,
                $payment->customer->email,
                $this->templates->paymentPlaceholders($payment),
                $payment->customer_id,
                $payment->invoice_id,
                null,
                $payment->id
            );
        } catch (\Throwable $e) {
            Log::warning('sendPaymentReceipt: '.$e->getMessage());
        }
    }

    public function sendCustomerWelcome(Customer $customer): void
    {
        try {
            if (! $customer->email) {
                return;
            }

            $this->sendTemplated(
                ReminderType::CustomerWelcome,
                $customer->email,
                $this->templates->basePlaceholders($customer),
                $customer->id
            );
        } catch (\Throwable $e) {
            Log::warning('sendCustomerWelcome: '.$e->getMessage());
        }
    }

    /**
     * Manual / follow-up: uses manual_reminder or follow_up template slug when type is Manual/FollowUp.
     */
    public function sendManual(
        string $recipientEmail,
        string $subject,
        string $htmlBody,
        ?int $customerId = null,
        ?int $invoiceId = null,
        ?int $quotationId = null,
        bool $asFollowUp = false
    ): bool {
        $settings = ReminderSetting::current();
        if (! $settings->enable_manual_reminders) {
            return false;
        }
        if (! $this->validateEmail($recipientEmail)) {
            return false;
        }

        $type = $asFollowUp ? ReminderType::FollowUpReminder : ReminderType::ManualReminder;
        $text = Str::limit(trim(strip_tags(str_replace(['<br>', '<br/>', '<br />'], "\n", $htmlBody))), 10000);

        $cc = [];
        if ($settings->send_copy_to_admin) {
            $admin = \App\Models\CompanySetting::getSettings()->company_email ?? null;
            if ($admin && $this->validateEmail($admin) && strcasecmp($admin, $recipientEmail) !== 0) {
                $cc[] = $admin;
            }
        }

        $log = $this->logs->createPending([
            'customer_id' => $customerId,
            'invoice_id' => $invoiceId,
            'quotation_id' => $quotationId,
            'reminder_type' => $type->value,
            'subject' => $subject,
            'recipient_email' => $recipientEmail,
            'created_by' => auth()->id(),
        ]);

        $body = $this->templates->sanitizeBody($htmlBody);

        try {
            if ($settings->usesQueue()) {
                SendReminderEmailJob::dispatch($log->id, $subject, $body, $text, [], $cc);
            } else {
                SendReminderEmailJob::dispatchSync($log->id, $subject, $body, $text, [], $cc);
            }
        } catch (\Throwable $e) {
            $this->logs->markFailed($log, $e->getMessage());

            return false;
        }

        return true;
    }
}
