<?php
/**
 * smtp_mailer.php
 * Minimal, dependency-free SMTP client (no PHPMailer/Composer needed - just
 * raw sockets). Supports SSL (port 465) and STARTTLS (port 587), AUTH LOGIN.
 * Good enough for Gmail, Outlook, Zoho, SendGrid, Mailgun, or any standard
 * SMTP provider the admin configures in Website Manage.
 */

class SimpleSmtpMailer {
    private $host, $port, $username, $password, $encryption, $fromEmail, $fromName;
    public $lastError = '';

    public function __construct($settings) {
        $this->host       = $settings['smtp_host'] ?? '';
        $this->port       = (int)($settings['smtp_port'] ?? 587);
        $this->username   = $settings['smtp_username'] ?? '';
        $this->password   = $settings['smtp_password'] ?? '';
        $this->encryption = $settings['smtp_encryption'] ?? 'tls'; // 'tls' | 'ssl' | 'none'
        $this->fromEmail  = $settings['smtp_from_email'] ?? $this->username;
        $this->fromName   = $settings['smtp_from_name'] ?? 'NeoPay';
    }

    private function readResponse($socket) {
        $data = '';
        while ($line = fgets($socket, 515)) {
            $data .= $line;
            if (isset($line[3]) && $line[3] === ' ') break;
        }
        $meta = stream_get_meta_data($socket);
        if (!empty($meta['timed_out'])) {
            $this->lastError = 'SMTP server did not respond in time (connection timed out).';
        }
        return $data;
    }

    private function sendCommand($socket, $command, $expectedCode) {
        fwrite($socket, $command . "\r\n");
        $response = $this->readResponse($socket);
        $code = substr($response, 0, 3);
        if ($code !== (string)$expectedCode) {
            if ($response === '') {
                // lastError already set by readResponse() if this was a
                // timeout - don't clobber it with a less useful message.
                if (empty($this->lastError)) {
                    $this->lastError = "No response from SMTP server for command '$command'.";
                }
            } else {
                $this->lastError = "Command '$command' failed: $response";
            }
            return false;
        }
        return true;
    }

    /**
     * @param string $toEmail
     * @param string $toName
     * @param string $subject
     * @param string $htmlBody
     * @return bool
     */
    public function send($toEmail, $toName, $subject, $htmlBody) {
        if (empty($this->host) || empty($this->username)) {
            $this->lastError = 'SMTP not configured. Set it up in Website Manage -> Email (SMTP).';
            return false;
        }

        $transport = ($this->encryption === 'ssl') ? 'ssl://' : '';
        $socket = @stream_socket_client($transport . $this->host . ':' . $this->port, $errno, $errstr, 8);

        if (!$socket) {
            $this->lastError = "Could not connect to SMTP server: $errstr ($errno)";
            return false;
        }

        // Without this, a server that accepts the connection but never
        // replies (firewall silently dropping packets, wrong port, etc.)
        // makes fgets() block indefinitely - which is exactly what was
        // freezing the login page. 8 seconds max per read/write now.
        stream_set_timeout($socket, 8);

        $this->readResponse($socket); // greeting

        if (!$this->sendCommand($socket, "EHLO " . ($_SERVER['SERVER_NAME'] ?? 'localhost'), 250)) { fclose($socket); return false; }

        if ($this->encryption === 'tls') {
            if (!$this->sendCommand($socket, "STARTTLS", 220)) { fclose($socket); return false; }
            if (!stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
                $this->lastError = 'STARTTLS negotiation failed.';
                fclose($socket);
                return false;
            }
            if (!$this->sendCommand($socket, "EHLO " . ($_SERVER['SERVER_NAME'] ?? 'localhost'), 250)) { fclose($socket); return false; }
        }

        if (!$this->sendCommand($socket, "AUTH LOGIN", 334)) { fclose($socket); return false; }
        if (!$this->sendCommand($socket, base64_encode($this->username), 334)) { fclose($socket); return false; }
        if (!$this->sendCommand($socket, base64_encode($this->password), 235)) { fclose($socket); return false; }

        if (!$this->sendCommand($socket, "MAIL FROM:<{$this->fromEmail}>", 250)) { fclose($socket); return false; }
        if (!$this->sendCommand($socket, "RCPT TO:<{$toEmail}>", 250)) { fclose($socket); return false; }
        if (!$this->sendCommand($socket, "DATA", 354)) { fclose($socket); return false; }

        $toHeader = $toName ? "\"$toName\" <$toEmail>" : $toEmail;
        $fromHeader = "\"{$this->fromName}\" <{$this->fromEmail}>";
        $boundary = md5(uniqid());

        $headers  = "From: $fromHeader\r\n";
        $headers .= "To: $toHeader\r\n";
        $headers .= "Subject: =?UTF-8?B?" . base64_encode($subject) . "?=\r\n";
        $headers .= "MIME-Version: 1.0\r\n";
        $headers .= "Content-Type: text/html; charset=UTF-8\r\n";
        $headers .= "Date: " . date('r') . "\r\n";

        // Dot-stuff any lines that start with a lone "." per SMTP spec.
        $body = preg_replace('/^\./m', '..', $htmlBody);

        if (!$this->sendCommand($socket, $headers . "\r\n" . $body . "\r\n.", 250)) { fclose($socket); return false; }

        fwrite($socket, "QUIT\r\n");
        fclose($socket);
        return true;
    }
}

/**
 * Loads SMTP config from site_settings and sends a templated email.
 * Returns true/false; check smtp_last_error() for the reason on failure.
 */
$GLOBALS['__smtp_last_error'] = '';

function send_smtp_email($conn, $toEmail, $toName, $subject, $htmlBody) {
    $res = mysqli_query($conn, "SELECT * FROM site_settings LIMIT 1");
    $settings = $res ? mysqli_fetch_assoc($res) : [];
    $mailer = new SimpleSmtpMailer($settings ?: []);
    $ok = $mailer->send($toEmail, $toName, $subject, $htmlBody);
    $GLOBALS['__smtp_last_error'] = $mailer->lastError;
    return $ok;
}

function smtp_last_error() {
    return $GLOBALS['__smtp_last_error'];
}
