<?php

namespace App\Services\Email;

use App\Models\EmailDeliveryLog;
use App\Models\User;
use App\Services\Analytics\EngagementLogger;
use App\Services\Settings\SettingsService;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Mail;
use Throwable;

class TransactionalEmailSender
{
    public function __construct(
        private readonly SettingsService $settings,
        private readonly EngagementLogger $engagementLogger,
    ) {}

    /**
     * @param  array<string, string>  $headers
     * @param  array<string, mixed>  $metadata
     */
    public function send(
        string $toEmail,
        ?string $toName,
        string $subject,
        string $html,
        string $text,
        array $headers = [],
        ?int $userId = null,
        string $type = 'transactional',
        array $metadata = [],
        bool $resendable = false,
    ): EmailDeliveryLog {
        $provider = $this->settings->isEnabled('email.brevo_enabled') && filled($this->settings->get('email.brevo_api_key'))
            ? 'brevo'
            : 'laravel';

        $log = EmailDeliveryLog::query()->create([
            'user_id' => $userId,
            'type' => $type,
            'provider' => $provider,
            'recipient_email' => $toEmail,
            'recipient_name' => $toName,
            'subject' => $subject,
            'status' => EmailDeliveryLog::STATUS_PENDING,
            'resendable' => $resendable,
            'headers' => $headers === [] ? null : $headers,
            'metadata' => $metadata === [] ? null : $metadata,
        ]);

        try {
            if ($provider === 'brevo') {
                $messageId = $this->sendUsingBrevo($toEmail, $toName, $subject, $html, $text, $headers);
            } else {
                $this->sendUsingLaravelMailer($toEmail, $toName, $subject, $html, $headers);
                $messageId = null;
            }

            $log->forceFill([
                'status' => EmailDeliveryLog::STATUS_SENT,
                'provider_message_id' => $messageId,
                'brevo_message_id' => $provider === 'brevo' ? $messageId : null,
                'sent_at' => now(),
                'failed_at' => null,
                'failure_reason' => null,
            ])->save();

            $this->engagementLogger->record(
                $userId ? User::query()->find($userId) : null,
                'email_sent',
                "Sent {$type} email to {$toEmail}.",
                [
                    'email_delivery_log_id' => $log->id,
                    'provider' => $provider,
                    'subject' => $subject,
                ],
                $log,
            );

            return $log->refresh();
        } catch (Throwable $exception) {
            $log->forceFill([
                'status' => EmailDeliveryLog::STATUS_FAILED,
                'failed_at' => now(),
                'failure_reason' => $exception->getMessage(),
            ])->save();

            $this->engagementLogger->record(
                $userId ? User::query()->find($userId) : null,
                'email_failed',
                "Failed {$type} email to {$toEmail}.",
                [
                    'email_delivery_log_id' => $log->id,
                    'provider' => $provider,
                    'subject' => $subject,
                    'failure_reason' => $exception->getMessage(),
                ],
                $log,
            );

            throw $exception;
        }
    }

    /**
     * @param  array<string, string>  $headers
     */
    private function sendUsingBrevo(
        string $toEmail,
        ?string $toName,
        string $subject,
        string $html,
        string $text,
        array $headers
    ): ?string {
        $response = Http::timeout(12)
            ->withHeaders([
                'accept' => 'application/json',
                'api-key' => (string) $this->settings->get('email.brevo_api_key'),
                'content-type' => 'application/json',
            ])
            ->post((string) $this->settings->get('email.brevo_api_url', 'https://api.brevo.com/v3/smtp/email'), array_filter([
                'sender' => [
                    'name' => (string) $this->settings->get('email.from_name', 'RemoteMatch'),
                    'email' => (string) $this->settings->get('email.from_email', 'no-reply@remotematch.local'),
                ],
                'to' => [
                    array_filter([
                        'email' => $toEmail,
                        'name' => $toName,
                    ], fn (mixed $value): bool => filled($value)),
                ],
                'replyTo' => filled($this->settings->get('email.reply_to_email'))
                    ? ['email' => (string) $this->settings->get('email.reply_to_email')]
                    : null,
                'subject' => $subject,
                'htmlContent' => $html,
                'textContent' => $text,
                'headers' => $headers === [] ? null : $headers,
            ], fn (mixed $value): bool => $value !== null))
            ->throw();

        $payload = $response->json();

        return is_array($payload)
            ? (string) ($payload['messageId'] ?? $payload['message-id'] ?? $payload['id'] ?? '')
            : null;
    }

    /**
     * @param  array<string, string>  $headers
     */
    private function sendUsingLaravelMailer(
        string $toEmail,
        ?string $toName,
        string $subject,
        string $html,
        array $headers
    ): void {
        $this->applyMailSettings();

        Mail::html(
            $html,
            function ($message) use ($toEmail, $toName, $subject, $headers): void {
                $message
                    ->to($toEmail, $toName)
                    ->subject($subject);

                if (filled($this->settings->get('email.reply_to_email'))) {
                    $message->replyTo((string) $this->settings->get('email.reply_to_email'));
                }

                foreach ($headers as $name => $value) {
                    $message->getHeaders()->addTextHeader($name, $value);
                }
            }
        );
    }

    private function applyMailSettings(): void
    {
        config([
            'mail.default' => $this->settings->get('email.mail_driver', config('mail.default')),
            'mail.mailers.smtp.host' => $this->settings->get('email.smtp_host', config('mail.mailers.smtp.host')),
            'mail.mailers.smtp.port' => $this->settings->get('email.smtp_port', config('mail.mailers.smtp.port')),
            'mail.mailers.smtp.username' => $this->settings->get('email.smtp_username', config('mail.mailers.smtp.username')),
            'mail.mailers.smtp.password' => $this->settings->get('email.smtp_password', config('mail.mailers.smtp.password')),
            'mail.mailers.smtp.encryption' => $this->settings->get('email.smtp_encryption', config('mail.mailers.smtp.encryption')),
            'mail.from.address' => $this->settings->get('email.from_email', config('mail.from.address')),
            'mail.from.name' => $this->settings->get('email.from_name', config('mail.from.name')),
        ]);
    }
}
