<?php

declare(strict_types=1);

namespace ApiModel\Infrastructure\Mail;

use PHPMailer\PHPMailer\PHPMailer;

/**
 * SMTP con PHPMailer, alineado con api_modelo (variables EMAIL_*) y con el estilo de {@see api_modelo\Controller\Notify}.
 * No incluye Send_Code / Send_Message (dependen de tablas auth.message y auth.users del modelo).
 */
final class PhpMailerFactory
{
    private function __construct(
        private readonly string $host,
        private readonly int $port,
        private readonly string $user,
        private readonly string $password,
        private readonly string $encryption,
        private readonly int $smtpDebug,
        private readonly bool $sslVerify,
        private readonly string $notificationFrom,
    ) {
    }

    public static function isEnvConfigured(): bool
    {
        return trim((string) ($_ENV['EMAIL_HOST'] ?? '')) !== ''
            && trim((string) ($_ENV['EMAIL_USER'] ?? '')) !== ''
            && trim((string) ($_ENV['EMAIL_PASSWORD'] ?? '')) !== '';
    }

    public static function tryFromEnv(): ?self
    {
        if (! self::isEnvConfigured()) {
            return null;
        }

        $host = trim((string) ($_ENV['EMAIL_HOST'] ?? ''));
        $user = trim((string) ($_ENV['EMAIL_USER'] ?? ''));
        $password = (string) ($_ENV['EMAIL_PASSWORD'] ?? '');
        $notif = trim((string) ($_ENV['EMAIL_NOTIF'] ?? ''));
        if ($notif === '') {
            $notif = $user;
        }

        $port = (int) ($_ENV['EMAIL_PORT'] ?? 465);
        if ($port < 1 || $port > 65535) {
            $port = 465;
        }

        $encryption = strtolower(trim((string) ($_ENV['EMAIL_ENCRYPTION'] ?? 'ssl')));
        if (! in_array($encryption, ['ssl', 'tls', 'none', ''], true)) {
            $encryption = 'ssl';
        }

        $smtpDebug = (int) ($_ENV['EMAIL_SMTP_DEBUG'] ?? 0);
        if ($smtpDebug < 0) {
            $smtpDebug = 0;
        }

        $sslVerify = self::envFlagTruthy($_ENV['EMAIL_SSL_VERIFY'] ?? '1');

        return new self($host, $port, $user, $password, $encryption, $smtpDebug, $sslVerify, $notif);
    }

    /**
     * @throws \RuntimeException si faltan variables obligatorias
     */
    public static function fromEnv(): self
    {
        $f = self::tryFromEnv();
        if ($f === null) {
            throw new \RuntimeException(
                'Correo SMTP no configurado: definen EMAIL_HOST, EMAIL_USER y EMAIL_PASSWORD.',
            );
        }

        return $f;
    }

    /**
     * Mismo rol que el envío con cuenta “principal” en Notify::Send_Code (From = EMAIL_USER).
     */
    public function createForUserMail(): PHPMailer
    {
        return $this->createConfigured($this->user);
    }

    /**
     * Mismo rol que Notify::Send_Message (From = EMAIL_NOTIF o EMAIL_USER si no hay NOTIF).
     */
    public function createForNotifications(): PHPMailer
    {
        return $this->createConfigured($this->notificationFrom);
    }

    private function createConfigured(string $fromAddress): PHPMailer
    {
        $mail = new PHPMailer(true);
        $mail->isSMTP();
        $mail->Host = $this->host;
        $mail->SMTPDebug = $this->smtpDebug;
        $mail->Debugoutput = 'error_log';
        $mail->Port = $this->port;
        $mail->SMTPAuth = true;
        $mail->CharSet = PHPMailer::CHARSET_UTF8;
        $mail->Username = $fromAddress;
        $mail->Password = $this->password;

        if ($this->encryption === 'tls') {
            $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
        } elseif ($this->encryption === 'ssl') {
            $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
        } else {
            $mail->SMTPSecure = '';
            $mail->SMTPAutoTLS = false;
        }

        if (! $this->sslVerify) {
            $mail->SMTPOptions = [
                'ssl' => [
                    'verify_peer' => false,
                    'verify_peer_name' => false,
                    'allow_self_signed' => true,
                ],
            ];
        }

        $mail->setFrom($fromAddress, '', false);

        return $mail;
    }

    /**
     * @param mixed $raw
     */
    private static function envFlagTruthy($raw): bool
    {
        $v = strtolower(trim((string) $raw));

        return ! in_array($v, ['0', 'false', 'no', 'off', ''], true);
    }
}
