<?php
/**
 * @intranet-file common/mailsender.php
 * @intranet-file-version 2026.03.27.1
 * @intranet-file-date 2026-03-27
 * @intranet-file-owner CSIF Intranet
 */

declare(strict_types=1);

require_once __DIR__ . '/composer_loader.php';
intranet_require_composer(['phpmailer/phpmailer']);

use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\PHPMailer;

class MailSender
{
    private PHPMailer $mail;

    public function __construct()
    {
        $this->mail = new PHPMailer(true);
        $this->configure();
    }

    private function configure(): void
    {
        $host = trim((string) $this->configValue('SMTP_HOST', 'smtp.dominioabsoluto.net'));
        $port = (int) $this->configValue('SMTP_PORT', 587);
        $username = trim((string) $this->configValue('SMTP_USER', ''));
        $password = (string) $this->configValue('SMTP_PASS', '');

        $secureMode = strtolower(trim((string) $this->configValue('SMTP_SECURE', 'tls')));
        $smtpDebug = (int) $this->configValue('SMTP_DEBUG', 0);
        $timeout = (int) $this->configValue('SMTP_TIMEOUT', 15);

        $this->mail->isSMTP();
        $this->mail->Host = $host;
        $this->mail->SMTPAuth = true;
        $this->mail->Username = $username;
        $this->mail->Password = $password;
        $this->mail->Port = $port;
        $this->mail->Timeout = $timeout;
        $this->mail->CharSet = 'UTF-8';
        $this->mail->SMTPDebug = $smtpDebug;

        switch ($secureMode) {
            case 'ssl':
            case 'smtps':
                $this->mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
                break;

            case 'none':
            case '':
                $this->mail->SMTPSecure = '';
                break;

            case 'tls':
            case 'starttls':
            default:
                $this->mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
                break;
        }

        /**
         * Solo usar esto en entornos de prueba si fuese necesario.
         * En producción, lo correcto es arreglar certificados/CA.
         */
        if ((string) $this->configValue('SMTP_ALLOW_SELF_SIGNED', '0') === '1') {
            $this->mail->SMTPOptions = [
                'ssl' => [
                    'verify_peer' => false,
                    'verify_peer_name' => false,
                    'allow_self_signed' => true,
                ],
            ];
        }
    }

    public function getErrorInfo(): string
    {
        return $this->mail->ErrorInfo ?? '';
    }

    private function configValue(string $name, $default = '')
    {
        if (defined($name)) {
            $value = constant($name);
            if ($value !== null && $value !== '') {
                return $value;
            }
        }

        $env = getenv($name);
        if ($env !== false && $env !== '') {
            return $env;
        }

        return $default;
    }

    /**
     * Envía un email.
     *
     * Formato esperado:
     * - $to, $cc, $bcc: ['correo@dominio.com' => 'Nombre']
     * - $attachments: ['/ruta/fichero.ext' => 'nombre_visible.ext']
     */
    public function sendEmail(
        string $fromEmail,
        string $fromName,
        array $to,
        string $subject,
        string $bodyHtml,
        string $altBody = '',
        array $cc = [],
        array $bcc = [],
        array $attachments = []
    ): bool {
        try {
            $this->resetMessage();

            $resolvedFromEmail = trim((string) (
                $this->configValue('MAIL_NOREPLY_EMAIL')
                ?: $this->configValue('SMTP_FROM')
                ?: $this->configValue('SMTP_USER')
                ?: 'no-reply@localhost'
            ));

            $resolvedFromName = trim((string) (
                $this->configValue('MAIL_NOREPLY_NAME')
                ?: $this->configValue('SMTP_FROM_NAME')
                ?: (function_exists('intranet_site_name') ? intranet_site_name() : 'Intranet')
            ));

            if (!filter_var($resolvedFromEmail, FILTER_VALIDATE_EMAIL)) {
                throw new RuntimeException(
                    'La dirección de remitente configurada no es válida: ' . $resolvedFromEmail
                );
            }

            $this->mail->setFrom($resolvedFromEmail, $resolvedFromName);
            $this->mail->addReplyTo($resolvedFromEmail, $resolvedFromName);
            $this->mail->addCustomHeader('Auto-Submitted', 'auto-generated');
            $this->mail->addCustomHeader('X-Auto-Response-Suppress', 'All');

            $addedTo = $this->addRecipients($to, 'to');
            $this->addRecipients($cc, 'cc');
            $this->addRecipients($bcc, 'bcc');
            $this->addAttachments($attachments);

            if ($addedTo === 0) {
                throw new RuntimeException('No se ha indicado ningún destinatario válido.');
            }

            $this->mail->isHTML(true);
            $this->mail->Subject = trim($subject);
            $this->mail->Body = $bodyHtml;
            $this->mail->AltBody = $altBody !== ''
                ? $altBody
                : trim(html_entity_decode(strip_tags($bodyHtml), ENT_QUOTES | ENT_HTML5, 'UTF-8'));

            $toList = $this->formatRecipientList($this->mail->getToAddresses());

            $this->mail->send();

            error_log('[MAIL] OK to=' . implode(', ', $toList) . ' subject=' . $this->mail->Subject);

            return true;
        } catch (Exception $e) {
            $toList = $this->formatRecipientList($this->mail->getToAddresses());
            error_log('[MAIL] FAIL to=' . implode(', ', $toList) . ' subject=' . ($this->mail->Subject ?? '') . ' err=' . $e->getMessage());
            error_log('[MAIL] ErrorInfo=' . ($this->mail->ErrorInfo ?? ''));

            return false;
        } catch (Throwable $e) {
            $toList = $this->formatRecipientList($this->mail->getToAddresses());
            error_log('[MAIL] FAIL to=' . implode(', ', $toList) . ' subject=' . ($this->mail->Subject ?? '') . ' err=' . $e->getMessage());

            return false;
        }
    }

    private function resetMessage(): void
    {
        $this->mail->clearAllRecipients();
        $this->mail->clearAttachments();
        $this->mail->clearReplyTos();
        $this->mail->clearCustomHeaders();
    }

    private function addRecipients(array $recipients, string $type = 'to'): int
    {
        $count = 0;

        foreach ($recipients as $email => $name) {
            $email = trim((string) $email);
            $name = trim((string) $name);

            if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
                error_log('[MAIL] Destinatario descartado por email no válido: ' . $email);
                continue;
            }

            switch ($type) {
                case 'cc':
                    $this->mail->addCC($email, $name);
                    break;

                case 'bcc':
                    $this->mail->addBCC($email, $name);
                    break;

                case 'to':
                default:
                    $this->mail->addAddress($email, $name);
                    break;
            }

            $count++;
        }

        return $count;
    }

    private function addAttachments(array $attachments): void
    {
        foreach ($attachments as $filePath => $fileName) {
            $filePath = (string) $filePath;
            $fileName = trim((string) $fileName);

            if (!is_file($filePath) || !is_readable($filePath)) {
                error_log('[MAIL] Adjunto descartado por no existir o no ser legible: ' . $filePath);
                continue;
            }

            if ($fileName !== '') {
                $this->mail->addAttachment($filePath, $fileName);
            } else {
                $this->mail->addAttachment($filePath);
            }
        }
    }

    private function formatRecipientList(array $addresses): array
    {
        $result = [];

        foreach ($addresses as $addr) {
            $email = $addr[0] ?? '';
            $name = $addr[1] ?? '';

            if ($email === '') {
                continue;
            }

            $result[] = $email . ($name !== '' ? ' (' . $name . ')' : '');
        }

        return $result;
    }
}
