<?php

namespace App\Services\Email;

use App\Models\EmailConfiguration;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Mail;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;

class EmailConfigurationService
{
    public function get(): EmailConfiguration
    {
        return EmailConfiguration::query()->firstOrCreate([], [
            'provider' => 'smtp',
            'encryption' => 'tls',
            'from_name' => config('southend.app_name'),
            'from_address' => config('mail.from.address'),
        ]);
    }

    /**
     * @return array<string, mixed>
     */
    public function toPublicArray(EmailConfiguration $config): array
    {
        return [
            'id' => $config->id,
            'provider' => $config->provider,
            'smtp_host' => $config->smtp_host,
            'smtp_port' => $config->smtp_port,
            'smtp_username' => $config->smtp_username,
            'encryption' => $config->encryption,
            'from_name' => $config->from_name,
            'from_address' => $config->from_address,
            'reply_to' => $config->reply_to,
            'default_header' => $config->default_header,
            'default_footer' => $config->default_footer,
            'signature' => $config->signature,
            'logo_path' => $config->logo_path,
            'logo_url' => $config->logo_path ? asset('storage/'.$config->logo_path) : null,
            'is_enabled' => $config->is_enabled,
            'automated_enabled' => $config->automated_enabled,
            'api_domain' => $config->api_domain,
            'api_region' => $config->api_region,
            'has_password' => $config->hasPassword(),
            'has_api_key' => $config->hasApiKey(),
        ];
    }

    /**
     * @param  array<string, mixed>  $data
     */
    public function update(array $data): EmailConfiguration
    {
        $config = $this->get();

        if (empty($data['smtp_password'])) {
            unset($data['smtp_password']);
        }

        if (empty($data['api_key'])) {
            unset($data['api_key']);
        }

        if (empty($data['api_secret'])) {
            unset($data['api_secret']);
        }

        $config->fill($data);
        $this->applyProviderDefaults($config);
        $config->save();

        return $config->fresh();
    }

    public function applyProviderDefaults(EmailConfiguration $config): void
    {
        match ($config->provider) {
            'gmail' => $config->fill([
                'smtp_host' => $config->smtp_host ?: 'smtp.gmail.com',
                'smtp_port' => $config->smtp_port ?: 587,
                'encryption' => $config->encryption ?: 'tls',
            ]),
            'outlook' => $config->fill([
                'smtp_host' => $config->smtp_host ?: 'smtp.office365.com',
                'smtp_port' => $config->smtp_port ?: 587,
                'encryption' => $config->encryption ?: 'tls',
            ]),
            'sendgrid' => $config->fill([
                'smtp_host' => $config->smtp_host ?: 'smtp.sendgrid.net',
                'smtp_port' => $config->smtp_port ?: 587,
                'smtp_username' => $config->smtp_username ?: 'apikey',
                'encryption' => $config->encryption ?: 'tls',
            ]),
            default => null,
        };
    }

    public function isReady(?EmailConfiguration $config = null): bool
    {
        $config ??= $this->get();

        if (! $config->is_enabled) {
            return false;
        }

        if (! filled($config->from_address)) {
            return false;
        }

        return match ($config->provider) {
            'mailgun' => filled($config->api_key) && filled($config->api_domain),
            'ses' => filled($config->api_key) && filled($config->api_secret),
            default => filled($config->smtp_host) && filled($config->smtp_port),
        };
    }

    public function canSendAutomated(?EmailConfiguration $config = null): bool
    {
        $config ??= $this->get();

        return $this->isReady($config) && $config->automated_enabled;
    }

    public function applyRuntimeConfig(?EmailConfiguration $config = null): string
    {
        $config ??= $this->get();

        $mailer = match ($config->provider) {
            'mailgun' => 'mailgun',
            'ses' => 'ses',
            default => 'southend_smtp',
        };

        if ($mailer === 'southend_smtp') {
            Config::set('mail.mailers.southend_smtp', [
                'transport' => 'smtp',
                'host' => $config->smtp_host,
                'port' => $config->smtp_port,
                'encryption' => $config->encryption === 'none' ? null : $config->encryption,
                'username' => $config->smtp_username,
                'password' => $config->smtp_password,
                'timeout' => 30,
            ]);
        }

        if ($config->provider === 'mailgun') {
            Config::set('services.mailgun.domain', $config->api_domain);
            Config::set('services.mailgun.secret', $config->api_key);
        }

        if ($config->provider === 'ses') {
            Config::set('services.ses.key', $config->api_key);
            Config::set('services.ses.secret', $config->api_secret);
            Config::set('services.ses.region', $config->api_region ?: 'us-east-1');
        }

        if ($config->provider === 'sendgrid' && $config->hasApiKey()) {
            Config::set('mail.mailers.southend_smtp.password', $config->api_key);
        }

        Config::set('mail.from.address', $config->from_address ?: config('mail.from.address'));
        Config::set('mail.from.name', $config->from_name ?: config('southend.app_name'));

        return $mailer === 'southend_smtp' ? 'southend_smtp' : $mailer;
    }

    /**
     * @throws TransportExceptionInterface
     */
    public function testConnection(string $recipient): void
    {
        $config = $this->get();

        if (! $this->isReady($config)) {
            throw new \RuntimeException('Email is not configured. Save valid settings and enable email sending first.');
        }

        $mailer = $this->applyRuntimeConfig($config);

        Mail::mailer($mailer)->raw(
            'This is a test email from '.config('southend.app_name').'. Your email configuration is working correctly.',
            function ($message) use ($recipient, $config) {
                $message->to($recipient)
                    ->subject('Test Email — '.config('southend.app_name'))
                    ->from($config->from_address, $config->from_name);

                if ($config->reply_to) {
                    $message->replyTo($config->reply_to);
                }
            }
        );
    }

    public function validateSmtpConnection(EmailConfiguration $config): bool
    {
        if ($config->provider !== 'smtp' && ! in_array($config->provider, ['gmail', 'outlook', 'sendgrid', 'custom'], true)) {
            return true;
        }

        $transport = new EsmtpTransport(
            (string) $config->smtp_host,
            (int) $config->smtp_port,
            $config->encryption === 'ssl',
        );

        if ($config->smtp_username) {
            $transport->setUsername($config->smtp_username);
        }

        if ($config->smtp_password) {
            $transport->setPassword($config->smtp_password);
        }

        $transport->start();

        return true;
    }
}
