<?php

declare(strict_types=1);

namespace GalpaoImperial\Api;

final class Mailer
{
    private static ?string $lastError = null;

    public static function lastError(): ?string
    {
        return self::$lastError;
    }

    public static function sendHtml(string $to, string $subject, string $html, ?string $replyTo = null, ?string $plainText = null): bool
    {
        self::$lastError = null;
        $to = trim($to);
        if ($to === '' || !filter_var($to, FILTER_VALIDATE_EMAIL)) {
            self::$lastError = 'Destinatario invalido.';
            return false;
        }

        $host = $_SERVER['HTTP_HOST'] ?? 'galpaoimperial.minhabagg.com.br';
        $fromEmail = env('MAIL_FROM_EMAIL', 'ingressos@' . preg_replace('/^www\./', '', $host)) ?? 'ingressos@galpaoimperial.minhabagg.com.br';
        $fromName = env('MAIL_FROM_NAME', 'Galpao Imperial') ?? 'Galpao Imperial';
        $replyTo = $replyTo ?: (env('MAIL_REPLY_TO', $fromEmail) ?? $fromEmail);

        $driver = strtolower(env('MAIL_DRIVER', 'smtp') ?? 'smtp');
        if ($driver === 'mail') {
            if (!function_exists('mail')) {
                self::$lastError = 'A funcao mail() esta desativada na hospedagem. Use SMTP.';
                return false;
            }
            return self::sendWithMailFunction($to, $subject, $html, $fromEmail, $fromName, $replyTo, $plainText);
        }

        $sent = self::sendWithSmtp($to, $subject, $html, $fromEmail, $fromName, $replyTo, $plainText);
        if ($sent) {
            return true;
        }

        $smtpError = self::$lastError;
        $fallbackDriver = strtolower(env('MAIL_SMTP_FALLBACK', 'mail') ?? 'mail');
        if ($fallbackDriver === 'mail' && function_exists('mail')) {
            $sentByMail = self::sendWithMailFunction($to, $subject, $html, $fromEmail, $fromName, $replyTo, $plainText);
            if ($sentByMail) {
                self::$lastError = null;
                return true;
            }
            self::$lastError = trim((string) $smtpError . ' | Fallback mail(): ' . (self::$lastError ?? 'falhou.'));
        }

        return false;
    }

    private static function sendWithMailFunction(string $to, string $subject, string $html, string $fromEmail, string $fromName, string $replyTo, ?string $plainText = null): bool
    {
        $encodedSubject = self::encodeHeader($subject);
        $fromHeader = self::encodeHeader($fromName) . ' <' . $fromEmail . '>';
        $body = self::hasPlainText($plainText) ? self::buildAlternativeBody($html, (string) $plainText) : $html;
        $headers = [
            'MIME-Version: 1.0',
            self::hasPlainText($plainText)
                ? 'Content-Type: multipart/alternative; boundary="' . self::boundary() . '"'
                : 'Content-Type: text/html; charset=UTF-8',
            self::hasPlainText($plainText) ? null : 'Content-Transfer-Encoding: 8bit',
            'From: ' . $fromHeader,
            'Reply-To: ' . $replyTo,
            'X-Mailer: GalpaoImperial',
        ];
        $headers = array_values(array_filter($headers));

        $envelope = filter_var($fromEmail, FILTER_VALIDATE_EMAIL) ? '-f' . $fromEmail : '';
        $sent = $envelope !== ''
            ? @mail($to, $encodedSubject, $body, implode("\r\n", $headers), $envelope)
            : @mail($to, $encodedSubject, $body, implode("\r\n", $headers));

        if (!$sent) {
            self::$lastError = 'Falha no mail() da hospedagem.';
        }

        return $sent;
    }

    private static function sendWithSmtp(string $to, string $subject, string $html, string $fromEmail, string $fromName, string $replyTo, ?string $plainText = null): bool
    {
        $host = env('MAIL_HOST', 'mail.galpaoimperial.minhabagg.com.br') ?? 'mail.galpaoimperial.minhabagg.com.br';
        $port = (int) (env('MAIL_PORT', '465') ?? '465');
        $secure = strtolower(env('MAIL_SECURE', 'ssl') ?? 'ssl');
        $username = env('MAIL_USERNAME', $fromEmail) ?? $fromEmail;
        $password = env('MAIL_PASSWORD', '') ?? '';
        $timeout = 20;

        if ($password === '' || str_contains($password, 'COLOQUE_')) {
            self::$lastError = 'MAIL_PASSWORD nao configurado no .env.';
            return false;
        }

        $remote = ($secure === 'ssl' ? 'ssl://' : '') . $host . ':' . $port;
        $errno = 0;
        $errstr = '';
        $socket = @stream_socket_client($remote, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT);
        if (!$socket) {
            self::$lastError = 'Nao conectou no SMTP: ' . $errstr . ' (' . $errno . ')';
            return false;
        }

        stream_set_timeout($socket, $timeout);

        try {
            self::expect($socket, [220], 'banner SMTP');
            self::command($socket, 'EHLO ' . self::localhostName(), [250], 'EHLO');

            if ($secure === 'tls' || $secure === 'starttls') {
                self::command($socket, 'STARTTLS', [220], 'STARTTLS');
                if (!@stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
                    throw new \RuntimeException('Falha ao ativar TLS no SMTP.');
                }
                self::command($socket, 'EHLO ' . self::localhostName(), [250], 'EHLO TLS');
            }

            self::command($socket, 'AUTH LOGIN', [334], 'AUTH LOGIN');
            self::command($socket, base64_encode($username), [334], 'SMTP usuario');
            self::command($socket, base64_encode($password), [235], 'SMTP senha');

            self::command($socket, 'MAIL FROM:<' . $fromEmail . '>', [250], 'MAIL FROM');
            self::command($socket, 'RCPT TO:<' . $to . '>', [250, 251], 'RCPT TO');
            self::command($socket, 'DATA', [354], 'DATA');

            $message = self::buildMessage($to, $subject, $html, $fromEmail, $fromName, $replyTo, $plainText);
            fwrite($socket, self::dotStuff($message) . "\r\n.\r\n");
            self::expect($socket, [250], 'envio DATA');
            self::command($socket, 'QUIT', [221, 250], 'QUIT');
            fclose($socket);
            return true;
        } catch (\Throwable $throwable) {
            self::$lastError = $throwable->getMessage();
            @fclose($socket);
            return false;
        }
    }

    private static function buildMessage(string $to, string $subject, string $html, string $fromEmail, string $fromName, string $replyTo, ?string $plainText = null): string
    {
        $fromHeader = self::encodeHeader($fromName) . ' <' . $fromEmail . '>';
        $hasPlainText = self::hasPlainText($plainText);
        $headers = [
            'Date: ' . date('r'),
            'To: <' . $to . '>',
            'From: ' . $fromHeader,
            'Reply-To: ' . $replyTo,
            'Subject: ' . self::encodeHeader($subject),
            'MIME-Version: 1.0',
            $hasPlainText
                ? 'Content-Type: multipart/alternative; boundary="' . self::boundary() . '"'
                : 'Content-Type: text/html; charset=UTF-8',
            $hasPlainText ? null : 'Content-Transfer-Encoding: 8bit',
            'X-Mailer: GalpaoImperial',
        ];
        $headers = array_values(array_filter($headers));

        $body = $hasPlainText ? self::buildAlternativeBody($html, (string) $plainText) : $html;
        return implode("\r\n", $headers) . "\r\n\r\n" . $body;
    }

    private static function buildAlternativeBody(string $html, string $plainText): string
    {
        $boundary = self::boundary();
        return implode("\r\n", [
            '--' . $boundary,
            'Content-Type: text/plain; charset=UTF-8',
            'Content-Transfer-Encoding: 8bit',
            '',
            $plainText,
            '--' . $boundary,
            'Content-Type: text/html; charset=UTF-8',
            'Content-Transfer-Encoding: 8bit',
            '',
            $html,
            '--' . $boundary . '--',
            '',
        ]);
    }

    private static function hasPlainText(?string $plainText): bool
    {
        return $plainText !== null && trim($plainText) !== '';
    }

    private static function boundary(): string
    {
        return 'galpao_imperial_' . substr(hash('sha256', __FILE__), 0, 16);
    }

    private static function command($socket, string $command, array $expectedCodes, string $context): string
    {
        fwrite($socket, $command . "\r\n");
        return self::expect($socket, $expectedCodes, $context);
    }

    private static function expect($socket, array $expectedCodes, string $context): string
    {
        $response = '';
        while (($line = fgets($socket, 515)) !== false) {
            $response .= $line;
            if (preg_match('/^([0-9]{3})(\s|-)/', $line, $matches) && $matches[2] === ' ') {
                $code = (int) $matches[1];
                if (!in_array($code, $expectedCodes, true)) {
                    throw new \RuntimeException('SMTP erro em ' . $context . ': ' . trim($response));
                }
                return $response;
            }
        }

        throw new \RuntimeException('SMTP sem resposta em ' . $context . '.');
    }

    private static function dotStuff(string $message): string
    {
        $message = str_replace(["\r\n", "\r"], "\n", $message);
        $lines = explode("\n", $message);
        foreach ($lines as &$line) {
            if (str_starts_with($line, '.')) {
                $line = '.' . $line;
            }
        }
        return implode("\r\n", $lines);
    }

    private static function encodeHeader(string $value): string
    {
        return '=?UTF-8?B?' . base64_encode($value) . '?=';
    }

    private static function localhostName(): string
    {
        $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
        return preg_replace('/[^a-zA-Z0-9.-]/', '', $host) ?: 'localhost';
    }
}
