<?php
/**
 * CY Consultancy — Minimal Gmail SMTP Mailer
 * Works on XAMPP with PHP 7.4+ (uses stream_socket_client + STARTTLS)
 */
class CYMailer {
    public string $host     = 'smtp.gmail.com';
    public int    $port     = 587;
    public string $username = '';
    public string $password = '';
    public string $fromEmail= '';
    public string $fromName = '';
    public string $lastError= '';

    public function send(string $toEmail, string $toName, string $subject, string $htmlBody, string $textBody = ''): bool {
        if (!$textBody) $textBody = strip_tags($htmlBody);

        // Build MIME message
        $boundary = '----=_Part_' . md5(uniqid());
        $headers  = implode("\r\n", [
            "From: {$this->fromName} <{$this->fromEmail}>",
            "To: {$toName} <{$toEmail}>",
            "Subject: =?UTF-8?B?" . base64_encode($subject) . "?=",
            "MIME-Version: 1.0",
            "Content-Type: multipart/alternative; boundary=\"{$boundary}\"",
            "X-Mailer: CYConsultancy/1.0",
        ]);

        $body = "--{$boundary}\r\n"
              . "Content-Type: text/plain; charset=UTF-8\r\n"
              . "Content-Transfer-Encoding: base64\r\n\r\n"
              . chunk_split(base64_encode($textBody)) . "\r\n"
              . "--{$boundary}\r\n"
              . "Content-Type: text/html; charset=UTF-8\r\n"
              . "Content-Transfer-Encoding: base64\r\n\r\n"
              . chunk_split(base64_encode($htmlBody)) . "\r\n"
              . "--{$boundary}--";

        $message = $headers . "\r\n\r\n" . $body;

        try {
            // Connect plain first, then STARTTLS
            $ctx  = stream_context_create(['ssl' => [
                'verify_peer'       => false,
                'verify_peer_name'  => false,
                'allow_self_signed' => true,
            ]]);
            $conn = @stream_socket_client(
                "tcp://{$this->host}:{$this->port}",
                $errno, $errstr, 15,
                STREAM_CLIENT_CONNECT, $ctx
            );
            if (!$conn) {
                $this->lastError = "Connection failed: {$errstr} ({$errno})";
                return false;
            }
            stream_set_timeout($conn, 15);

            $r = function() use ($conn) {
                $out = '';
                while (($line = fgets($conn, 1024)) !== false) {
                    $out .= $line;
                    if (isset($line[3]) && $line[3] === ' ') break;
                }
                return $out;
            };
            $w = function(string $cmd) use ($conn) { fputs($conn, $cmd . "\r\n"); };

            $resp = $r(); // 220 greeting
            if (strpos($resp, '220') === false) {
                $this->lastError = "Bad greeting: {$resp}"; fclose($conn); return false;
            }

            $w("EHLO " . ($_SERVER['HTTP_HOST'] ?? 'localhost')); $r();
            $w("STARTTLS"); $resp = $r();
            if (strpos($resp, '220') === false) {
                $this->lastError = "STARTTLS failed: {$resp}"; fclose($conn); return false;
            }

            // Upgrade to TLS
            if (!stream_socket_enable_crypto($conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
                $this->lastError = "TLS handshake failed"; fclose($conn); return false;
            }

            $w("EHLO " . ($_SERVER['HTTP_HOST'] ?? 'localhost')); $r();
            $w("AUTH LOGIN"); $r();
            $w(base64_encode($this->username)); $r();
            $w(base64_encode($this->password));
            $resp = $r();
            if (strpos($resp, '235') === false) {
                $this->lastError = "Auth failed — check App Password. Response: {$resp}";
                fclose($conn); return false;
            }

            $w("MAIL FROM:<{$this->fromEmail}>"); $r();
            $w("RCPT TO:<{$toEmail}>"); $resp = $r();
            if (strpos($resp, '250') === false) {
                $this->lastError = "RCPT failed: {$resp}"; fclose($conn); return false;
            }

            $w("DATA"); $r();
            $w($message . "\r\n."); $resp = $r();
            $w("QUIT"); fclose($conn);

            if (strpos($resp, '250') === false) {
                $this->lastError = "Message rejected: {$resp}"; return false;
            }
            return true;

        } catch (\Throwable $e) {
            $this->lastError = $e->getMessage();
            return false;
        }
    }
}
