<?php
declare(strict_types=1);

/** Outbound mail (registration verify, password reset). Task 2.5 — uses config mail.* */
final class AuthMailer {
    private function __construct() {
    }

    public static function send(string $to, string $subject, string $bodyText): bool {
        $from = (string) cfg('mail.from', 'Android Cast <noreply@apps.f0xx.org>');
        $replyTo = (string) cfg('mail.reply_to', '');
        $transport = strtolower((string) cfg('mail.transport', 'smtp'));
        if ($transport === 'sendmail') {
            return self::sendMail($to, $subject, $bodyText, $from, $replyTo);
        }
        return self::sendSmtp($to, $subject, $bodyText, $from, $replyTo);
    }

    public static function sendVerifyEmail(string $to, string $verifyUrl): bool {
        $subject = 'Verify your Android Cast account';
        $body = "Open this link to verify your email (valid 24h):\n\n" . $verifyUrl . "\n";
        return self::send($to, $subject, $body);
    }

    private static function sendMail(string $to, string $subject, string $body, string $from, string $replyTo): bool {
        $headers = "From: {$from}\r\n";
        if ($replyTo !== '') {
            $headers .= "Reply-To: {$replyTo}\r\n";
        }
        $headers .= "Content-Type: text/plain; charset=UTF-8\r\n";
        return @mail($to, $subject, $body, $headers);
    }

    private static function sendSmtp(string $to, string $subject, string $body, string $from, string $replyTo): bool {
        $host = (string) cfg('mail.smtp.host', '127.0.0.1');
        $port = (int) cfg('mail.smtp.port', 587);
        $enc = strtolower((string) cfg('mail.smtp.encryption', 'tls'));
        $user = (string) cfg('mail.smtp.username', '');
        $pass = (string) cfg('mail.smtp.password', '');
        $remote = ($enc === 'ssl' ? 'ssl://' : '') . $host;
        $errno = 0;
        $errstr = '';
        $fp = @stream_socket_client($remote . ':' . $port, $errno, $errstr, 15);
        if ($fp === false) {
            error_log('AuthMailer SMTP connect failed: ' . $errstr);
            return self::sendMail($to, $subject, $body, $from, $replyTo);
        }
        stream_set_timeout($fp, 15);
        if (!self::smtpExpect($fp, [220])) {
            fclose($fp);
            return false;
        }
        $ehloHost = 'localhost';
        fwrite($fp, "EHLO {$ehloHost}\r\n");
        if (!self::smtpExpect($fp, [250])) {
            fclose($fp);
            return false;
        }
        if ($enc === 'tls') {
            fwrite($fp, "STARTTLS\r\n");
            if (!self::smtpExpect($fp, [220])) {
                fclose($fp);
                return false;
            }
            if (!stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
                fclose($fp);
                return false;
            }
            fwrite($fp, "EHLO {$ehloHost}\r\n");
            if (!self::smtpExpect($fp, [250])) {
                fclose($fp);
                return false;
            }
        }
        if ($user !== '') {
            fwrite($fp, "AUTH LOGIN\r\n");
            if (!self::smtpExpect($fp, [334])) {
                fclose($fp);
                return false;
            }
            fwrite($fp, base64_encode($user) . "\r\n");
            if (!self::smtpExpect($fp, [334])) {
                fclose($fp);
                return false;
            }
            fwrite($fp, base64_encode($pass) . "\r\n");
            if (!self::smtpExpect($fp, [235])) {
                fclose($fp);
                return false;
            }
        }
        $fromAddr = self::extractAddress($from);
        fwrite($fp, "MAIL FROM:<{$fromAddr}>\r\n");
        if (!self::smtpExpect($fp, [250])) {
            fclose($fp);
            return false;
        }
        fwrite($fp, "RCPT TO:<{$to}>\r\n");
        if (!self::smtpExpect($fp, [250, 251])) {
            fclose($fp);
            return false;
        }
        fwrite($fp, "DATA\r\n");
        if (!self::smtpExpect($fp, [354])) {
            fclose($fp);
            return false;
        }
        $msg = "From: {$from}\r\n";
        if ($replyTo !== '') {
            $msg .= "Reply-To: {$replyTo}\r\n";
        }
        $msg .= "To: {$to}\r\nSubject: {$subject}\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n{$body}\r\n.\r\n";
        fwrite($fp, $msg);
        if (!self::smtpExpect($fp, [250])) {
            fclose($fp);
            return false;
        }
        fwrite($fp, "QUIT\r\n");
        fclose($fp);
        return true;
    }

    /** @param resource $fp @param list<int> $codes */
    private static function smtpExpect($fp, array $codes): bool {
        $line = '';
        while (($chunk = fgets($fp, 512)) !== false) {
            $line .= $chunk;
            if (strlen($chunk) >= 4 && $chunk[3] === ' ') {
                break;
            }
        }
        $code = (int) substr(trim($line), 0, 3);
        return in_array($code, $codes, true);
    }

    private static function extractAddress(string $from): string {
        if (preg_match('/<([^>]+)>/', $from, $m)) {
            return trim($m[1]);
        }
        return trim($from);
    }
}
