<?php

declare(strict_types=1);

require_once __DIR__ . '/Env.php';
require_once __DIR__ . '/Logger.php';

class Mailer
{
    public function send(string $subject, string $body, ?string $recipient = null): bool
    {
        $to = $recipient ?? Env::get('EMAIL_TO');
        $from = Env::get('EMAIL_FROM');

        if ($to === null || $from === null) {
            throw new RuntimeException('EMAIL_FROM and EMAIL_TO must be configured');
        }

        $smtpHost = Env::get('SMTP_HOST');
        if ($smtpHost) {
            Logger::log('info', sprintf('Mailer using SMTP transport (%s:%s)', $smtpHost, Env::get('SMTP_PORT', '587')));
            return $this->sendViaSmtp($to, $from, $subject, $body);
        }

        $headers = implode("\r\n", [
            'From: ' . $from,
            'MIME-Version: 1.0',
            'Content-Type: text/plain; charset=UTF-8',
        ]);

        Logger::log('info', 'Mailer using mail() fallback transport');
        return mail($to, $subject, $body, $headers);
    }

    private function sendViaSmtp(string $to, string $from, string $subject, string $body): bool
    {
        $host = Env::get('SMTP_HOST');
        $port = Env::get('SMTP_PORT', '587');
        $user = Env::get('SMTP_USER');
        $pass = Env::get('SMTP_PASS');

        if ($host === null) {
            throw new RuntimeException('SMTP_HOST is required for SMTP mail sending');
        }

        $fp = stream_socket_client($host . ':' . $port, $errno, $errstr, 10);
        if ($fp === false) {
            throw new RuntimeException(sprintf('Unable to connect to SMTP server: %s', $errstr));
        }

        $this->expectResponse($fp, 220);
        $this->sendLine($fp, 'EHLO blog-engine');

        $needsAuth = $user !== null && $pass !== null && $user !== '' && $pass !== '';
        if ($needsAuth) {
            $this->sendLine($fp, 'AUTH LOGIN', 334);
            $this->sendLine($fp, base64_encode($user), 334);
            $this->sendLine($fp, base64_encode($pass), 235);
        }

        $this->sendLine($fp, 'MAIL FROM:<' . $from . '>', 250);
        $this->sendLine($fp, 'RCPT TO:<' . $to . '>', 250);
        $this->sendLine($fp, 'DATA', 354);

        $message = implode("\r\n", [
            'Subject: ' . $subject,
            'From: ' . $from,
            'To: ' . $to,
            'MIME-Version: 1.0',
            'Content-Type: text/plain; charset=UTF-8',
            '',
            $body,
            '.',
        ]);

        $this->sendLine($fp, $message, 250);
        $this->sendLine($fp, 'QUIT', 221);
        fclose($fp);

        return true;
    }

    private function sendLine($fp, string $line, int $expectCode = 250): void
    {
        fwrite($fp, $line . "\r\n");
        $this->expectResponse($fp, $expectCode);
    }

    private function expectResponse($fp, int $code): void
    {
        $response = '';
        while (is_resource($fp) && !feof($fp)) {
            $responseLine = fgets($fp);
            if ($responseLine === false) {
                break;
            }

            $response .= $responseLine;
            if (preg_match('/^\d{3} /', $responseLine)) {
                break;
            }
        }

        if ((int)substr($response, 0, 3) !== $code) {
            throw new RuntimeException(sprintf('SMTP server error: %s', trim($response)));
        }
    }
}
