<?php

namespace App\Services;

use App\Models\AppNotification;
use App\Models\Customer;
use App\Models\Tenant;
use Illuminate\Support\Facades\Log;
use Throwable;

class MemberNotificationAutomationService
{
    public function __construct(
        private readonly SmsService $sms,
        private readonly WhatsappService $whatsapp,
        private readonly PlatformSettings $settings,
    ) {
    }

    public static function channels(): array
    {
        return [
            'portal' => ['label' => 'Portal', 'icon' => 'bi-bell'],
            'email' => ['label' => 'Email', 'icon' => 'bi-envelope'],
            'sms' => ['label' => 'SMS', 'icon' => 'bi-phone'],
            'whatsapp' => ['label' => 'WhatsApp', 'icon' => 'bi-whatsapp'],
        ];
    }

    public static function catalog(): array
    {
        return [
            'loan_approved' => [
                'label' => 'Loan approved',
                'description' => 'Notify the member when a loan application is approved.',
                'default_enabled' => true,
                'default_channels' => ['portal'],
                'default_subject' => 'Loan approved',
                'default_body' => 'Hello {member_name}, your loan {loan_number} for {principal} has been approved by {company_name}.',
            ],
            'loan_disbursed' => [
                'label' => 'Loan disbursed',
                'description' => 'Notify the member when approved funds are released.',
                'default_enabled' => true,
                'default_channels' => ['portal'],
                'default_subject' => 'Loan disbursed',
                'default_body' => 'Hello {member_name}, your loan {loan_number} has been disbursed. Amount: {amount}. Total repayable: {total_payable}.',
            ],
            'loan_repayment_received' => [
                'label' => 'Loan repayment received',
                'description' => 'Notify the member after a repayment is posted.',
                'default_enabled' => true,
                'default_channels' => ['portal'],
                'default_subject' => 'Loan repayment received',
                'default_body' => 'Hello {member_name}, we received your repayment of {amount} for loan {loan_number}. Outstanding balance: {balance_after}.',
            ],
            'savings_account_opened' => [
                'label' => 'Savings opened',
                'description' => 'Notify the member when a savings account is opened or approved.',
                'default_enabled' => true,
                'default_channels' => ['portal'],
                'default_subject' => 'Savings account opened',
                'default_body' => 'Hello {member_name}, your {product_name} savings account {account_number} is now active with {company_name}.',
            ],
            'savings_deposit_approved' => [
                'label' => 'Savings deposit approved',
                'description' => 'Notify the member when a pending savings deposit is approved.',
                'default_enabled' => true,
                'default_channels' => ['portal'],
                'default_subject' => 'Savings deposit approved',
                'default_body' => 'Hello {member_name}, your savings deposit of {amount} has been approved. New balance: {balance_after}.',
            ],
            'savings_withdrawal_posted' => [
                'label' => 'Savings withdrawal posted',
                'description' => 'Notify the member when a savings withdrawal is recorded.',
                'default_enabled' => true,
                'default_channels' => ['portal'],
                'default_subject' => 'Savings withdrawal posted',
                'default_body' => 'Hello {member_name}, your savings withdrawal of {amount} has been posted. New balance: {balance_after}.',
            ],
            'investment_deposit_approved' => [
                'label' => 'Investment deposit approved',
                'description' => 'Notify the member when an investment deposit is approved.',
                'default_enabled' => true,
                'default_channels' => ['portal'],
                'default_subject' => 'Investment deposit approved',
                'default_body' => 'Hello {member_name}, your investment deposit of {amount} has been approved. Investment balance: {balance_after}.',
            ],
            'investment_withdrawal_posted' => [
                'label' => 'Investment withdrawal posted',
                'description' => 'Notify the member when an investment withdrawal or maturity payout is recorded.',
                'default_enabled' => true,
                'default_channels' => ['portal'],
                'default_subject' => 'Investment withdrawal posted',
                'default_body' => 'Hello {member_name}, your investment {transaction_type} of {amount} has been posted. New balance: {balance_after}.',
            ],
            'loan_repayment_due_reminder' => [
                'label' => 'Loan due reminder',
                'description' => 'Remind members when a loan repayment is due soon.',
                'default_enabled' => false,
                'default_channels' => ['portal', 'sms'],
                'default_subject' => 'Loan repayment due',
                'default_body' => 'Hello {member_name}, your loan {loan_number} repayment of {due_amount} is due on {due_date}. Please pay on time to avoid penalties.',
            ],
            'loan_repayment_overdue_reminder' => [
                'label' => 'Overdue loan reminder',
                'description' => 'Remind members whose loan repayment is overdue.',
                'default_enabled' => false,
                'default_channels' => ['portal', 'sms'],
                'default_subject' => 'Loan repayment overdue',
                'default_body' => 'Hello {member_name}, your loan {loan_number} repayment of {due_amount} is overdue by {days_overdue} day(s). Kindly make payment as soon as possible.',
            ],
            'savings_contribution_due_reminder' => [
                'label' => 'Savings contribution reminder',
                'description' => 'Remind members about due thrift, target, Isusu, or cooperative savings contributions.',
                'default_enabled' => false,
                'default_channels' => ['portal', 'sms'],
                'default_subject' => 'Savings contribution due',
                'default_body' => 'Hello {member_name}, your {product_name} contribution of {due_amount} is due on {due_date}.',
            ],
            'savings_maturity_reminder' => [
                'label' => 'Savings maturity reminder',
                'description' => 'Remind members when a target, thrift, Isusu, or cooperative savings account is maturing soon.',
                'default_enabled' => false,
                'default_channels' => ['portal'],
                'default_subject' => 'Savings maturing soon',
                'default_body' => 'Hello {member_name}, your {product_name} savings account {account_number} matures on {maturity_date}.',
            ],
            'investment_maturity_reminder' => [
                'label' => 'Investment maturity reminder',
                'description' => 'Remind members when an investment is maturing soon.',
                'default_enabled' => false,
                'default_channels' => ['portal'],
                'default_subject' => 'Investment maturing soon',
                'default_body' => 'Hello {member_name}, your investment account {account_number} matures on {maturity_date}. Expected value: {amount}.',
            ],
            'kyc_pending_reminder' => [
                'label' => 'KYC reminder',
                'description' => 'Remind members to complete or update their KYC information.',
                'default_enabled' => false,
                'default_channels' => ['portal', 'sms'],
                'default_subject' => 'Complete your KYC',
                'default_body' => 'Hello {member_name}, please complete your KYC with {company_name} so your account can stay fully active. Current KYC status: {kyc_status}.',
            ],
        ];
    }

    public static function placeholders(): array
    {
        return [
            '{member_name}' => 'Member full name',
            '{first_name}' => 'Member first name',
            '{company_name}' => 'Company name',
            '{amount}' => 'Transaction amount',
            '{principal}' => 'Loan principal',
            '{total_payable}' => 'Total loan repayable',
            '{balance_after}' => 'Balance after transaction',
            '{loan_number}' => 'Loan ID',
            '{account_number}' => 'Savings or investment account',
            '{product_name}' => 'Product name',
            '{reference}' => 'Transaction reference',
            '{transaction_type}' => 'Withdrawal or payout type',
            '{due_amount}' => 'Expected amount due',
            '{expected_amount}' => 'Expected savings or investment amount',
            '{due_date}' => 'Loan or contribution due date',
            '{maturity_date}' => 'Savings or investment maturity date',
            '{days_left}' => 'Days left before due/maturity date',
            '{days_overdue}' => 'Days overdue',
            '{kyc_status}' => 'Member KYC status',
            '{date}' => 'Current date',
        ];
    }

    public static function defaultSettings(): array
    {
        return self::normalizeSettings([]);
    }

    public static function normalizeSettings(array $settings): array
    {
        $validChannels = array_keys(self::channels());

        return collect(self::catalog())
            ->mapWithKeys(function (array $event, string $key) use ($settings, $validChannels): array {
                $hasSavedEvent = array_key_exists($key, $settings);
                $eventSettings = is_array($settings[$key] ?? null) ? $settings[$key] : [];
                $channels = $hasSavedEvent ? ($eventSettings['channels'] ?? []) : $event['default_channels'];
                $channels = is_array($channels) ? $channels : [];
                $subject = trim((string) ($eventSettings['subject'] ?? '')) ?: $event['default_subject'];
                $body = trim((string) ($eventSettings['body'] ?? '')) ?: $event['default_body'];

                return [
                    $key => [
                        'enabled' => filter_var($hasSavedEvent ? ($eventSettings['enabled'] ?? false) : $event['default_enabled'], FILTER_VALIDATE_BOOLEAN),
                        'channels' => array_values(array_intersect($validChannels, $channels)),
                        'subject' => $subject,
                        'body' => $body,
                    ],
                ];
            })
            ->all();
    }

    public function send(?Customer $customer, string $event, array $context = []): void
    {
        if (! $customer) {
            return;
        }

        $customer->loadMissing(['tenant', 'user']);
        $tenant = $customer->tenant;

        if (! $tenant) {
            return;
        }

        $eventSettings = $this->eventSettings($tenant, $event);

        if (! ($eventSettings['enabled'] ?? false) || empty($eventSettings['channels'])) {
            return;
        }

        $message = $this->message($tenant, $customer, $event, $context, $eventSettings);

        foreach ($eventSettings['channels'] as $channel) {
            if (! $customer->allowsNotificationChannel($channel)) {
                continue;
            }

            $this->sendChannel($channel, $tenant, $customer, $message['subject'], $message['body'], $event);
        }
    }

    public function isEnabled(Tenant $tenant, string $event): bool
    {
        $settings = $this->eventSettings($tenant, $event);

        return (bool) ($settings['enabled'] ?? false) && ! empty($settings['channels']);
    }

    private function eventSettings(Tenant $tenant, string $event): array
    {
        $settings = self::normalizeSettings($tenant->settings['member_notification_automations'] ?? []);

        return $settings[$event] ?? ['enabled' => false, 'channels' => []];
    }

    private function sendChannel(string $channel, Tenant $tenant, Customer $customer, string $subject, string $body, string $event): void
    {
        try {
            match ($channel) {
                'portal' => $this->sendPortal($tenant, $customer, $subject, $body, $event),
                'email' => $this->sendEmail($tenant, $customer, $subject, $body),
                'sms' => filled($customer->phone) ? $this->sms->send($customer->phone, $body, $tenant, $customer, ['source' => 'member_automation', 'event' => $event]) : null,
                'whatsapp' => filled($customer->phone) ? $this->whatsapp->send($customer->phone, $body, $tenant, $customer, ['source' => 'member_automation', 'event' => $event]) : null,
                default => null,
            };
        } catch (Throwable $exception) {
            report($exception);
            Log::warning('Automatic member notification failed.', [
                'tenant_id' => $tenant->id,
                'customer_id' => $customer->id,
                'event' => $event,
                'channel' => $channel,
                'error' => $exception->getMessage(),
            ]);
        }
    }

    private function sendPortal(Tenant $tenant, Customer $customer, string $subject, string $body, string $event): void
    {
        if (! $customer->user_id) {
            return;
        }

        AppNotification::withoutGlobalScopes()->create([
            'tenant_id' => $tenant->id,
            'user_id' => $customer->user_id,
            'title' => $subject,
            'body' => $body,
            'type' => $event,
            'channel' => 'database',
        ]);
    }

    private function sendEmail(Tenant $tenant, Customer $customer, string $subject, string $body): void
    {
        if (blank($customer->email)) {
            return;
        }

        $result = $this->settings->sendRawMail($tenant, $customer->email, $customer->fullName(), $subject, $body);

        if ($result['status'] === 'failed') {
            throw new \RuntimeException($result['message']);
        }
    }

    private function message(Tenant $tenant, Customer $customer, string $event, array $context, array $eventSettings): array
    {
        $catalog = self::catalog();
        $default = $catalog[$event] ?? [
            'default_subject' => 'Account notification',
            'default_body' => 'Hello {member_name}, there is a new update on your account with {company_name}.',
        ];

        $subject = trim((string) ($eventSettings['subject'] ?? '')) ?: $default['default_subject'];
        $body = trim((string) ($eventSettings['body'] ?? '')) ?: $default['default_body'];

        return [
            'subject' => $this->renderTemplate($tenant, $customer, $subject, $context),
            'body' => $this->renderTemplate($tenant, $customer, $body, $context),
        ];
    }

    private function renderTemplate(Tenant $tenant, Customer $customer, string $template, array $context): string
    {
        $name = $customer->fullName() ?: 'Member';
        $amount = $context['amount'] ?? $context['principal'] ?? $context['total_payable'] ?? 0;

        return strtr($template, [
            '{member_name}' => $name,
            '{first_name}' => $customer->first_name ?: $name,
            '{company_name}' => $tenant->name,
            '{amount}' => $this->money($tenant, (float) $amount),
            '{principal}' => $this->money($tenant, (float) ($context['principal'] ?? 0)),
            '{total_payable}' => $this->money($tenant, (float) ($context['total_payable'] ?? 0)),
            '{balance_after}' => $this->money($tenant, (float) ($context['balance_after'] ?? 0)),
            '{loan_number}' => (string) ($context['loan_number'] ?? '-'),
            '{account_number}' => (string) ($context['account_number'] ?? '-'),
            '{product_name}' => (string) ($context['product_name'] ?? 'savings'),
            '{reference}' => (string) ($context['reference'] ?? '-'),
            '{transaction_type}' => (string) ($context['type'] ?? $context['transaction_type'] ?? 'withdrawal'),
            '{due_amount}' => $this->money($tenant, (float) ($context['due_amount'] ?? $context['amount'] ?? 0)),
            '{expected_amount}' => $this->money($tenant, (float) ($context['expected_amount'] ?? $context['amount'] ?? 0)),
            '{due_date}' => $this->dateValue($context['due_date'] ?? null),
            '{maturity_date}' => $this->dateValue($context['maturity_date'] ?? null),
            '{days_left}' => (string) ($context['days_left'] ?? '-'),
            '{days_overdue}' => (string) ($context['days_overdue'] ?? '-'),
            '{kyc_status}' => str((string) ($context['kyc_status'] ?? '-'))->headline()->toString(),
            '{date}' => app_date(now()),
        ]);
    }

    private function dateValue(mixed $value): string
    {
        if ($value instanceof \DateTimeInterface) {
            return app_date($value);
        }

        return filled($value) ? (string) $value : '-';
    }

    private function money(Tenant $tenant, float $amount): string
    {
        $currency = $tenant->settings['currency'] ?? 'NGN';

        return $currency.' '.number_format($amount, 2);
    }
}
