<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\Logger;
use App\Models\SmtpSettingsModel;
use PHPMailer\PHPMailer\Exception as PHPMailerException;
use PHPMailer\PHPMailer\PHPMailer;

/**
 * MailService
 *
 * Thin wrapper around PHPMailer so every module (password resets, user
 * invitations, campaign sends, appointment reminders, personal emails)
 * shares one configured transport instead of each reaching for `mail()`
 * or building its own PHPMailer instance. Requires PHPMailer to be
 * installed via Composer:
 *
 *   composer require phpmailer/phpmailer
 *
 * Transport config resolution order (Phase 4): the `smtp_settings`
 * table (admin-configured via the SMTP Settings page) wins if a row
 * exists and `is_configured = 1`; otherwise falls back to
 * config/mail.php (.env-driven), exactly as it did in Phase 1. This
 * means an install with nothing configured in the UI yet still works
 * from .env, and an admin can later override it from the UI without
 * a deploy.
 *
 * This class intentionally does NOT decide what triggers an email or
 * what the email says — that belongs to each feature module. It only
 * knows how to actually send one, given a subject/body/recipient.
 */
final class MailService
{
    public function send(string $toEmail, string $toName, string $subject, string $htmlBody, ?string $textBody = null, ?string $fromEmailOverride = null, ?int $businessLineId = null): bool
    {
        $config = $this->resolveConfig();

        if (!class_exists(PHPMailer::class)) {
            Logger::error('PHPMailer is not installed. Run `composer require phpmailer/phpmailer`.');

            return false;
        }

        $htmlBody = (new EmailSignatureService())->appendSignature($htmlBody, $businessLineId);

        $mailer = new PHPMailer(true);

        try {
            $mailer->CharSet = PHPMailer::CHARSET_UTF8;
            $mailer->Encoding = PHPMailer::ENCODING_BASE64;

            $mailer->isSMTP();
            $mailer->Host = $config['host'];
            $mailer->Port = $config['port'];
            $mailer->SMTPAuth = $config['username'] !== '';
            $mailer->Username = $config['username'];
            $mailer->Password = $config['password'];

            if ($config['encryption']) {
                $mailer->SMTPSecure = $config['encryption'];
            }

            $mailer->setFrom($fromEmailOverride ?? $config['from']['address'], $config['from']['name']);
            $mailer->addAddress($toEmail, $toName);

            $mailer->isHTML(true);
            $mailer->Subject = $subject;
            $mailer->Body = $htmlBody;
            $mailer->AltBody = $textBody ?? strip_tags($htmlBody);

            $mailer->send();

            return true;
        } catch (PHPMailerException $e) {
            Logger::error('Mail send failed', [
                'to' => $toEmail,
                'subject' => $subject,
                'error' => $mailer->ErrorInfo,
            ]);

            return false;
        }
    }

    /** Renders app/views/emails/{$template}.php with $data in scope, then sends it. */
    public function sendTemplate(string $toEmail, string $toName, string $subject, string $template, array $data = []): bool
    {
        $viewPath = dirname(__DIR__) . "/views/emails/{$template}.php";

        if (!is_file($viewPath)) {
            Logger::error("Email template not found: {$template}");

            return false;
        }

        $render = function () use ($viewPath, $data): string {
            extract($data, EXTR_SKIP);
            ob_start();
            require $viewPath;

            return (string) ob_get_clean();
        };

        return $this->send($toEmail, $toName, $subject, $render());
    }

    /** @return array{host:string,port:int,username:string,password:string,encryption:?string,from:array{address:string,name:string}} */
    private function resolveConfig(): array
    {
        $dbSettings = (new SmtpSettingsModel())->get();

        if ($dbSettings !== null && (int) $dbSettings['is_configured'] === 1) {
            return [
                'host' => (string) $dbSettings['host'],
                'port' => (int) $dbSettings['port'],
                'username' => (string) ($dbSettings['username'] ?? ''),
                'password' => (string) ($dbSettings['password'] ?? ''),
                'encryption' => $dbSettings['encryption'] === 'none' ? null : $dbSettings['encryption'],
                'from' => [
                    'address' => (string) $dbSettings['from_address'],
                    'name' => (string) $dbSettings['from_name'],
                ],
            ];
        }

        return config('mail');
    }
}
