<?php
namespace App\Services;

use App\Core\Config;

/**
 * Minimal dependency-free SMTP client (AUTH LOGIN, STARTTLS/SSL). Written
 * by hand rather than pulling in a library so the whole application can be
 * dropped onto a cPanel account with nothing beyond PHP + MySQL — no
 * Composer install step required (FR-SUP-113, NFR-23).
 */
class Mailer
{
    private $socket;
    private array $log = [];

    public static function send(string $toEmail, string $toName, string $subject, string $body): bool
    {
        $mailer = new self();
        try {
            $mailer->deliver($toEmail, $toName, $subject, $body);
            return true;
        } catch (\Throwable $e) {
            error_log('Mailer error: ' . $e->getMessage());
            return false;
        }
    }

    private function deliver(string $toEmail, string $toName, string $subject, string $body): void
    {
        $host = Config::get('smtp.host');
        $port = (int)Config::get('smtp.port', 587);
        $encryption = Config::get('smtp.encryption', 'tls');
        $fromEmail = Config::get('smtp.from_email');
        $fromName = Config::get('smtp.from_name');

        if (!$host || !$fromEmail) {
            throw new \RuntimeException('SMTP is not configured.');
        }

        $transport = $encryption === 'ssl' ? 'ssl://' . $host : $host;
        $this->socket = @fsockopen($transport, $port, $errno, $errstr, 10);
        if (!$this->socket) {
            throw new \RuntimeException("Could not connect to SMTP host: $errstr ($errno)");
        }
        $this->expect(220);

        $this->command('EHLO ' . gethostname());

        if ($encryption === 'tls') {
            $this->command('STARTTLS');
            $this->expect(220);
            if (!stream_socket_enable_crypto($this->socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
                throw new \RuntimeException('STARTTLS negotiation failed.');
            }
            $this->command('EHLO ' . gethostname());
        }

        $username = Config::get('smtp.username');
        $password = Config::get('smtp.password');
        if ($username) {
            $this->command('AUTH LOGIN');
            $this->expect(334);
            $this->command(base64_encode($username));
            $this->expect(334);
            $this->command(base64_encode($password));
            $this->expect(235);
        }

        $this->command('MAIL FROM:<' . $fromEmail . '>');
        $this->command('RCPT TO:<' . $toEmail . '>');
        $this->command('DATA');
        $this->expect(354);

        $headers = [
            'From: ' . $this->encodeHeader($fromName) . ' <' . $fromEmail . '>',
            'To: ' . $this->encodeHeader($toName) . ' <' . $toEmail . '>',
            'Subject: ' . $this->encodeHeader($subject),
            'MIME-Version: 1.0',
            'Content-Type: text/plain; charset=UTF-8',
            'Date: ' . date('r'),
        ];
        $message = implode("\r\n", $headers) . "\r\n\r\n" . $this->escapeBody($body) . "\r\n.";
        $this->command($message);
        $this->expect(250);
        $this->command('QUIT');
        fclose($this->socket);
    }

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

    private function escapeBody(string $body): string
    {
        // Dot-stuff lines that begin with a period, per RFC 5321.
        return preg_replace('/^\./m', '..', str_replace("\n", "\r\n", str_replace("\r\n", "\n", $body)));
    }

    private function command(string $cmd): void
    {
        fwrite($this->socket, $cmd . "\r\n");
    }

    private function expect(int $expectedCode): void
    {
        $response = '';
        while ($line = fgets($this->socket, 515)) {
            $response .= $line;
            if (isset($line[3]) && $line[3] === ' ') break;
        }
        $code = (int)substr($response, 0, 3);
        if ($code !== $expectedCode) {
            throw new \RuntimeException("SMTP error: expected {$expectedCode}, got: {$response}");
        }
    }
}
