<?php
// ============================================================
// Elegant Work — SMTP Mailer
// Pure PHP SMTP — no external dependencies required
// ============================================================

class EWGMailer {

    private string $host;
    private int    $port;
    private string $secure;   // tls | ssl | none
    private string $user;
    private string $pass;
    private string $fromEmail;
    private string $fromName;

    private $socket = null;
    public  string $lastError = '';
    public  array  $debugLog  = [];

    public function __construct(array $cfg) {
        $this->host      = $cfg['host']       ?? '';
        $this->port      = (int)($cfg['port'] ?? 587);
        $this->secure    = strtolower($cfg['secure']    ?? 'tls');
        $this->user      = $cfg['user']       ?? '';
        $this->pass      = $cfg['pass']       ?? '';
        $this->fromEmail = $cfg['from_email'] ?? '';
        $this->fromName  = $cfg['from_name']  ?? 'Elegant Work';
    }

    // ── Public: send a single email ─────────────────────────
    public function send(
        string $toEmail,
        string $toName,
        string $subject,
        string $htmlBody,
        ?string $attachPath = null,
        ?string $attachName = null
    ): bool {
        $this->debugLog = [];
        $this->lastError = '';

        try {
            if (!$this->connect()) return false;
            if (!$this->authenticate()) return false;
            if (!$this->sendMail($toEmail, $toName, $subject, $htmlBody, $attachPath, $attachName)) return false;
            $this->quit();
            return true;
        } catch (Exception $e) {
            $this->lastError = $e->getMessage();
            $this->log('EXCEPTION: ' . $e->getMessage());
            $this->quit();
            return false;
        }
    }

    // ── Connect to SMTP server ───────────────────────────────
    private function connect(): bool {
        $timeout = 15;
        if ($this->secure === 'ssl') {
            $addr = "ssl://{$this->host}:{$this->port}";
        } else {
            $addr = "tcp://{$this->host}:{$this->port}";
        }

        $this->socket = @stream_socket_client($addr, $errno, $errstr, $timeout);
        if (!$this->socket) {
            $this->lastError = "Connection failed: $errstr ($errno)";
            return false;
        }
        stream_set_timeout($this->socket, $timeout);

        $resp = $this->read();
        if (!$this->checkResp($resp, 220)) return false;

        // EHLO
        $resp = $this->cmd("EHLO " . gethostname());
        if (!$this->checkResp($resp, 250)) return false;

        // STARTTLS upgrade
        if ($this->secure === 'tls') {
            $resp = $this->cmd("STARTTLS");
            if (!$this->checkResp($resp, 220)) return false;

            if (!stream_socket_enable_crypto($this->socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
                $this->lastError = "STARTTLS crypto enable failed";
                return false;
            }
            // Re-EHLO after upgrade
            $resp = $this->cmd("EHLO " . gethostname());
            if (!$this->checkResp($resp, 250)) return false;
        }
        return true;
    }

    // ── Authenticate ─────────────────────────────────────────
    private function authenticate(): bool {
        $resp = $this->cmd("AUTH LOGIN");
        if (!$this->checkResp($resp, 334)) return false;

        $resp = $this->cmd(base64_encode($this->user));
        if (!$this->checkResp($resp, 334)) return false;

        $resp = $this->cmd(base64_encode($this->pass));
        if (!$this->checkResp($resp, 235)) {
            $this->lastError = "Authentication failed. Check SMTP username/password.";
            return false;
        }
        return true;
    }

    // ── Build and send message ───────────────────────────────
    private function sendMail(
        string $toEmail, string $toName, string $subject, string $htmlBody,
        ?string $attachPath, ?string $attachName
    ): bool {
        $resp = $this->cmd("MAIL FROM:<{$this->fromEmail}>");
        if (!$this->checkResp($resp, 250)) return false;

        $resp = $this->cmd("RCPT TO:<{$toEmail}>");
        if (!$this->checkResp($resp, [250, 251])) return false;

        $resp = $this->cmd("DATA");
        if (!$this->checkResp($resp, 354)) return false;

        $message = $this->buildMessage($toEmail, $toName, $subject, $htmlBody, $attachPath, $attachName);
        $this->write($message . "\r\n.");

        $resp = $this->read();
        if (!$this->checkResp($resp, 250)) return false;

        return true;
    }

    // ── Build MIME message ───────────────────────────────────
    private function buildMessage(
        string $toEmail, string $toName, string $subject, string $htmlBody,
        ?string $attachPath, ?string $attachName
    ): string {
        $boundary = '=_' . md5(uniqid('ewg', true));
        $fromFormatted = $this->fromName ? "=?UTF-8?B?" . base64_encode($this->fromName) . "?= <{$this->fromEmail}>" : $this->fromEmail;
        $toFormatted   = $toName ? "=?UTF-8?B?" . base64_encode($toName) . "?= <{$toEmail}>" : $toEmail;

        $hasAttach = $attachPath && file_exists($attachPath);
        $ct = $hasAttach ? "multipart/mixed" : "multipart/alternative";

        $headers  = "From: {$fromFormatted}\r\n";
        $headers .= "To: {$toFormatted}\r\n";
        $headers .= "Subject: =?UTF-8?B?" . base64_encode($subject) . "?=\r\n";
        $headers .= "MIME-Version: 1.0\r\n";
        $headers .= "Content-Type: {$ct}; boundary=\"{$boundary}\"\r\n";
        $headers .= "Date: " . date('r') . "\r\n";
        $headers .= "X-Mailer: EWG-Mailer/1.0\r\n";

        $body  = "--{$boundary}\r\n";
        if ($hasAttach) {
            // Wrap text parts in inner boundary
            $innerBound = '=_inner_' . md5(uniqid());
            $body .= "Content-Type: multipart/alternative; boundary=\"{$innerBound}\"\r\n\r\n";
            $body .= "--{$innerBound}\r\n";
            $body .= "Content-Type: text/plain; charset=UTF-8\r\n";
            $body .= "Content-Transfer-Encoding: quoted-printable\r\n\r\n";
            $body .= quoted_printable_encode(strip_tags($htmlBody)) . "\r\n\r\n";
            $body .= "--{$innerBound}\r\n";
            $body .= "Content-Type: text/html; charset=UTF-8\r\n";
            $body .= "Content-Transfer-Encoding: quoted-printable\r\n\r\n";
            $body .= quoted_printable_encode($htmlBody) . "\r\n\r\n";
            $body .= "--{$innerBound}--\r\n\r\n";

            // Attachment
            $fileData  = file_get_contents($attachPath);
            $mimeType  = mime_content_type($attachPath) ?: 'application/octet-stream';
            $safeName  = $attachName ?: basename($attachPath);
            $body .= "--{$boundary}\r\n";
            $body .= "Content-Type: {$mimeType}; name=\"{$safeName}\"\r\n";
            $body .= "Content-Transfer-Encoding: base64\r\n";
            $body .= "Content-Disposition: attachment; filename=\"{$safeName}\"\r\n\r\n";
            $body .= chunk_split(base64_encode($fileData)) . "\r\n";
        } else {
            $body .= "Content-Type: text/plain; charset=UTF-8\r\n";
            $body .= "Content-Transfer-Encoding: quoted-printable\r\n\r\n";
            $body .= quoted_printable_encode(strip_tags($htmlBody)) . "\r\n\r\n";
            $body .= "--{$boundary}\r\n";
            $body .= "Content-Type: text/html; charset=UTF-8\r\n";
            $body .= "Content-Transfer-Encoding: quoted-printable\r\n\r\n";
            $body .= quoted_printable_encode($htmlBody) . "\r\n";
        }
        $body .= "--{$boundary}--";

        return $headers . "\r\n" . $body;
    }

    // ── SMTP helpers ─────────────────────────────────────────
    private function cmd(string $cmd): string {
        $this->write($cmd);
        return $this->read();
    }

    private function write(string $data): void {
        $this->log(">> " . (strlen($data) > 200 ? substr($data, 0, 200) . '...[truncated]' : $data));
        fwrite($this->socket, $data . "\r\n");
    }

    private function read(): string {
        $resp = '';
        while ($line = fgets($this->socket, 515)) {
            $resp .= $line;
            if (substr($line, 3, 1) === ' ') break;
        }
        $this->log("<< " . trim($resp));
        return $resp;
    }

    private function checkResp(string $resp, $codes): bool {
        $code = (int)substr($resp, 0, 3);
        $allowed = is_array($codes) ? $codes : [$codes];
        if (!in_array($code, $allowed)) {
            $this->lastError = "SMTP error: " . trim($resp);
            return false;
        }
        return true;
    }

    private function quit(): void {
        if ($this->socket) {
            @fwrite($this->socket, "QUIT\r\n");
            @fclose($this->socket);
            $this->socket = null;
        }
    }

    private function log(string $msg): void {
        $this->debugLog[] = date('H:i:s') . ' ' . $msg;
    }
}

// ── Helper: load SMTP config from DB settings ─────────────
function getMailerConfig(PDO $db): array {
    $stmt = $db->query("SELECT setting_key, setting_value FROM settings WHERE setting_group = 'email'");
    $rows = $stmt->fetchAll();
    $cfg = [];
    foreach ($rows as $r) $cfg[$r['setting_key']] = $r['setting_value'];
    return $cfg;
}

// ── Helper: queue an email notification ───────────────────
function queueEmail(
    PDO    $db,
    string $type,
    string $toEmail,
    string $toName,
    string $subject,
    string $html,
    ?string $relatedType = null,
    ?int    $relatedId   = null,
    ?string $attachPath  = null,
    ?string $attachName  = null
): int {
    $stmt = $db->prepare("
        INSERT INTO email_queue
          (notification_type, recipient_email, recipient_name, subject, body_html,
           attachment_path, attachment_name, related_type, related_id)
        VALUES (?,?,?,?,?,?,?,?,?)
    ");
    $stmt->execute([
        $type, $toEmail, $toName, $subject, $html,
        $attachPath, $attachName, $relatedType, $relatedId
    ]);
    return (int)$db->lastInsertId();
}

// ── Helper: get notification rule ─────────────────────────
function getNotifRule(PDO $db, string $eventType): ?array {
    $stmt = $db->prepare("SELECT * FROM email_notification_rules WHERE event_type = ? LIMIT 1");
    $stmt->execute([$eventType]);
    $row = $stmt->fetch();
    return $row ?: null;
}

// ── Helper: get admin email ────────────────────────────────
function getAdminEmail(PDO $db): string {
    $stmt = $db->query("SELECT setting_value FROM settings WHERE setting_key = 'email_admin_address' LIMIT 1");
    return trim($stmt->fetchColumn() ?: '');
}

// ── Helper: generate a public PDF access token ────────────
// Returns a URL the client can click to view their PDF
function generatePdfAccessToken(PDO $db, int $jobCardId, string $type = 'client', string $appUrl = ''): string {
    // Clean up expired tokens first
    $db->exec("DELETE FROM pdf_access_tokens WHERE expires_at < NOW()");

    $token = bin2hex(random_bytes(32)); // 64-char hex
    $db->prepare("INSERT INTO pdf_access_tokens (token, job_card_id, type, expires_at) VALUES (?, ?, ?, DATE_ADD(NOW(), INTERVAL 90 DAY))")
       ->execute([$token, $jobCardId, $type]);

    return rtrim($appUrl, '/') . "/api/jobcards/pdf.php?pdf_token={$token}";
}