<?php
namespace App\Core;
/**
 * Minimal SMTP client (STARTTLS on 587) with no external dependencies.
 * Sufficient for transactional emails like password resets.
 */
class Mailer
{
    // NEW — purely additive. Stores the most recent failure reason so
    // callers can retrieve it after send() returns false, without
    // changing send()'s signature or return type for any existing caller.
    private static string $lastError = '';

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

    private static function config(): array
    {
        return require __DIR__ . '/../../config/mail.php';
    }
    public static function send(string $toEmail, string $toName, string $subject, string $htmlBody): bool
    {
        self::$lastError = '';
        $cfg = self::config();
        $host = $cfg['smtp_host'];
        $port = $cfg['smtp_port'];
        $user = $cfg['smtp_user'];
        $pass = $cfg['smtp_pass'];
        $from = $cfg['smtp_from'];
        $fromName = $cfg['smtp_from_name'];
        $errno = 0;
        $errstr = '';
        $socket = @stream_socket_client("tcp://{$host}:{$port}", $errno, $errstr, 15);
        if (!$socket) {
            self::$lastError = "connection failed - {$errstr} ({$errno})";
            error_log("Mailer: connection failed - {$errstr} ({$errno})");
            return false;
        }
        try {
            self::expect($socket, '220');
            self::command($socket, "EHLO {$host}", '250');
            self::command($socket, 'STARTTLS', '220');
            if (!stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
                self::$lastError = 'TLS negotiation failed';
                error_log('Mailer: TLS negotiation failed');
                fclose($socket);
                return false;
            }
            self::command($socket, "EHLO {$host}", '250');
            self::command($socket, 'AUTH LOGIN', '334');
            self::command($socket, base64_encode($user), '334');
            self::command($socket, base64_encode($pass), '235');
            self::command($socket, "MAIL FROM:<{$from}>", '250');
            self::command($socket, "RCPT TO:<{$toEmail}>", '250');
            self::command($socket, 'DATA', '354');
            $boundary = uniqid('hh_');
            $headers = [
                "From: {$fromName} <{$from}>",
                "To: {$toName} <{$toEmail}>",
                "Subject: {$subject}",
                'MIME-Version: 1.0',
                'Content-Type: text/html; charset=UTF-8',
                'Content-Transfer-Encoding: 8bit',
            ];
            $data = implode("\r\n", $headers) . "\r\n\r\n" . $htmlBody . "\r\n.";
            self::command($socket, $data, '250');
            self::command($socket, 'QUIT', '221');
            fclose($socket);
            return true;
        } catch (\Throwable $e) {
            self::$lastError = $e->getMessage();
            error_log('Mailer error: ' . $e->getMessage());
            if (is_resource($socket)) fclose($socket);
            return false;
        }
    }
    private static function command($socket, string $cmd, string $expectedCode): string
    {
        fwrite($socket, $cmd . "\r\n");
        return self::expect($socket, $expectedCode);
    }
    private static function expect($socket, string $expectedCode): string
    {
        $response = '';
        while ($line = fgets($socket, 515)) {
            $response .= $line;
            // multi-line SMTP responses continue until a line without a dash after the code
            if (preg_match('/^\d{3} /', $line)) break;
        }
        if (substr($response, 0, 3) !== $expectedCode) {
            throw new \RuntimeException("SMTP unexpected response: {$response}");
        }
        return $response;
    }
}
