<?php
declare(strict_types=1);

/**
 * cron/send_queue_worker.php
 * ---------------------------------------------------------------
 *  ▸ Versendet anstehende Queue‑Einträge paketweise (default: 10)
 *  ▸ Dynamische SMTP‑Rotation mit Domain‑Regeln & globalen Limits
 *  ▸ DKIM‑Schlüssel wird direkt aus der Datenbank geladen und als
 *    String (PHPMailer::DKIM_private_string) gesetzt – keine Temp‑Datei
 *  ▸ Vollständiges SQL‑Logging über ErrorLogger (Success/Error)
 * ---------------------------------------------------------------
 */

// ---------------------------------------------------------------------------
// 1. Bootstrap & Libraries
// ---------------------------------------------------------------------------
require_once __DIR__ . '/../includes/init.php';
require_once __DIR__ . '/../includes/libs/phpmailer/src/PHPMailer.php';
require_once __DIR__ . '/../includes/libs/phpmailer/src/SMTP.php';
require_once __DIR__ . '/../includes/libs/phpmailer/src/Exception.php';
require_once __DIR__ . '/../includes/core/SodiumCrypto.php'; // ▸ falls DKIM‑Key verschlüsselt

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

$db     = Database::getInstance();
$limit  = 10;                             // Zahl der Mails pro Durchlauf
$trackedLinkProcessor = new TrackedLinkProcessor($db, (string) ($config['app']['base_url'] ?? ''));

// ---------------------------------------------------------------------------
// 2. Helferfunktionen (SMTP‑Selektion & Send‑Logging)
// ---------------------------------------------------------------------------


function logDomainSend(PDO $db, int $smtp_id, string $domain): void
{
    try {
        $stmt = $db->prepare('INSERT INTO smtp_domain_sendlog (smtp_id, domain, sent_at)
                              VALUES (:sid, :dom, NOW())');
        $stmt->execute([':sid' => $smtp_id, ':dom' => $domain]);
        ErrorLogger::logSqlSuccess('send_queue_worker_s | file: ' . __FILE__ . ' | line: ' . __LINE__ . ' | message: Sendlog Domain ' . $domain . ' via smtp ' . $smtp_id);
    } catch (PDOException $e) {
        ErrorLogger::logSqlError('send_queue_worker_s | file: ' . __FILE__ . ' | line: ' . __LINE__ . ' | error: ' . $e->getMessage());
    }
}

// ✅ SMTP-Accounts laden und rotierend verwenden
$smtpAccounts = $db->query("SELECT * FROM smtp_accounts WHERE is_active = 1 ORDER BY last_used_at ASC")->fetchAll(PDO::FETCH_ASSOC);
if (empty($smtpAccounts)) {
    ErrorLogger::log("SEND_QUEUE", "Keine aktiven SMTP-Accounts gefunden.", 'ERROR');
    exit;
}

function rotateSmtp(&$accounts) {
    $smtp = array_shift($accounts);
    $accounts[] = $smtp;
    return $smtp;
}


// ---------------------------------------------------------------------------
// 3. Queue‑Auswahl
// ---------------------------------------------------------------------------
$sql = "SELECT q.id, q.recipient_id, q.template_id,
               t.subject, t.body_html,
               r.email, r.anrede, r.vorname, r.nachname,
               lst.from_identity_id, q.view_token, rl.pause_token,
               t.layout_id, l.editor_mode AS layout_editor_mode, l.source_content AS layout_source_content, l.header_html, l.footer_html
          FROM send_queue q
          JOIN email_templates t ON t.id = q.template_id
     LEFT JOIN email_layouts l  ON l.id = t.layout_id
          JOIN recipients r      ON r.id = q.recipient_id
          JOIN recipient_lists rl ON rl.recipient_id = r.id
          JOIN lists lst          ON lst.id = rl.list_id
         WHERE q.status   = 'pending'
           AND q.priority =  1
      ORDER BY q.priority DESC, q.created_at ASC
         LIMIT $limit";

$queueEntries = $db->query($sql)->fetchAll();

// ---------------------------------------------------------------------------
// 4. Haupt‑Versandschleife
// ---------------------------------------------------------------------------
foreach ($queueEntries as $entry) {
    try {
        /* 4.1 Eintrag locken -------------------------------------------- */ 
        $lock = $db->prepare('UPDATE send_queue
                                 SET status = "processing"
                               WHERE id     = :id
                                 AND status = "pending"');
        $lock->execute([':id' => $entry['id']]); 
        if ($lock->rowCount() === 0) continue; // schon von anderem Worker genommen

        $start    = microtime(true);
        $plainTxt = ''; 

        if (!empty($entry['layout_id'])) {
            $layoutState = mrPrepareLayoutEditorState([
                'editor_mode' => (string) ($entry['layout_editor_mode'] ?? 'html'),
                'source_content' => (string) ($entry['layout_source_content'] ?? ''),
                'header_html' => (string) ($entry['header_html'] ?? ''),
                'footer_html' => (string) ($entry['footer_html'] ?? ''),
            ]);
            $entry['header_html'] = $layoutState['header_html'];
            $entry['footer_html'] = $layoutState['footer_html'];
        }

        /* 4.2 Platzhalter / Personalisierung --------------------------- */
        $trackingPixel = '<img src="' . rtrim($config['app']['base_url'], '/') . '/open.php?id=' . $entry['id'] . '" width="1" height="1" style="display:none;" alt="">';
        $subject = $entry['subject'];
        $body    = ($entry['header_html'] ?? '') . $entry['body_html'] . $trackingPixel . ($entry['footer_html'] ?? '');

        $stdRepl = [
            '[email]'           => $entry['email'],
            '[anrede]'          => $entry['anrede'],
            '[vorname]'         => $entry['vorname'],
            '[nachname]'        => $entry['nachname'],
            '[confirm_url]'     => $config['app']['base_url'] . "/confirm.php?token={$entry['view_token']}",
            '[unsubscribe_url]' => $config['app']['base_url'] . "/unsubscribe.php?token={$entry['view_token']}",
            '[online_view_url]' => $config['app']['base_url'] . "/view.php?token={$entry['view_token']}",
            '[pause_url]'       => $config['app']['base_url'] . "/versandpause.php?token={$entry['pause_token']}"
        ];
        $subject = strtr($subject, $stdRepl);
        $body    = strtr($body,    $stdRepl);

        /* Custom‑Felder -------------------------------------------------- */
        $cst = $db->prepare('SELECT field_key, field_value FROM recipient_custom_fields WHERE recipient_id = :rid');
        $cst->execute([':rid' => $entry['recipient_id']]);
        foreach ($cst->fetchAll(PDO::FETCH_KEY_PAIR) as $k => $v) {
            $subject = str_replace('[field:' . $k . ']', $v, $subject);
            $body    = str_replace('[field:' . $k . ']', $v, $body);
        }

        /* Download‑Links tracken ---------------------------------------- */
        $body = preg_replace_callback('/\[download_pdf(\d+)\]/', function ($m) use ($db, $config, $entry) {
            $pdfId = (int) $m[1];
            $st    = $db->prepare('SELECT token FROM pdf_tokens WHERE pdf_id = :id ORDER BY created_at DESC LIMIT 1');
            $st->execute([':id' => $pdfId]);
            $tok = $st->fetchColumn();
            return $tok ? rtrim($config['app']['base_url'], '/') . '/download.php?token=' . $tok . '&rid=' . $entry['recipient_id'] : '';
        }, $body);

        /* Link-Tracking inkl. optionaler Link-Aktionen ------------------ */
        $body = $trackedLinkProcessor->processHtml($body, (int) $entry['id']);
        $body = mrWrapEmailShell($body);

        /* 4.3 Absender + DKIM-Daten holen ------------------------------- */
        $snd = $db->prepare('SELECT name, email, dkim_selector, dkim_private_key
                               FROM sender_identities
                              WHERE id = :id AND is_active = 1');
        $snd->execute([':id' => $entry['from_identity_id']]);
        $sender = $snd->fetch(PDO::FETCH_ASSOC);
        if (!$sender) throw new Exception('Absender nicht gefunden oder inaktiv.');

        // ✅ SMTP rotieren und setzen

        $smtp= rotateSmtp($smtpAccounts);
        $db->prepare("UPDATE smtp_accounts SET last_used_at = NOW() WHERE id = ?")->execute([$smtp['id']]);

        $domain  = substr(strrchr($entry['email'], '@'), 1);



        /* 4.5 PHPMailer config ----------------------------------------- */
        $mail = new PHPMailer(true);
        $mail->isSMTP();
        $mail->Host       = $smtp['smtp_host'];
        $mail->SMTPAuth   = true;
        $mail->Username   = $smtp['smtp_user'];
        $mail->Password   = $smtp['smtp_pass'];
        $mail->Port       = $smtp['smtp_port'];
        $mail->SMTPSecure = $smtp['smtp_secure'] ?: 'tls';
        $mail->CharSet    = 'UTF-8';
        $mail->Encoding  = 'base64';
        $mail->setFrom($sender['email'], $sender['name']);
        $mail->addAddress($entry['email']);
        $mail->Subject    = $subject;
        $mail->Body       = $body;
        $mail->AltBody    = strip_tags($body);
        $mail->isHTML(true);
        $mail->Sender     = $smtp['smtp_email'];           // Return‑Path
        $mail->addCustomHeader('X-Queue-ID', $entry['id']);
        $mail->addCustomHeader('List-Unsubscribe', '<' . $config['app']['base_url'] . '/unsubscribe.php?token=' . $entry['view_token'] . '>');

        //$mail->SMTPDebug  = 3;  // Debug Test bei einer Email


        /* DKIM ---------------------------------------------------------- */
        if (!empty($sender['dkim_selector']) && !empty($sender['dkim_private_key'])) {
            $mail->DKIM_domain         = substr(strrchr($sender['email'], '@'), 1);
            $mail->DKIM_selector       = $sender['dkim_selector'];
            $mail->DKIM_private_string = $sender['dkim_private_key'];
            $mail->DKIM_identity       = $sender['email'];

            // $mail->DKIM_canonicalization = 'relaxed/relaxed'; // optional
        }

        /* 4.6 Mail senden ---------------------------------------------- */
        $mail->send();

        /* 4.7 DB‑Status & Logging -------------------------------------- */
        $dur = (int) round((microtime(true) - $start) * 1000);
        $db->prepare('UPDATE send_queue
                         SET status = "sent",
                             sent_at = NOW(),
                             processing_time_ms = :ms
                       WHERE id = :id')
           ->execute([':ms' => $dur, ':id' => $entry['id']]);
        logDomainSend($db, $smtp['id'], $domain);

    } catch (Exception $e) {
        $errorMsg = $e->getMessage();

        // 🟡 Versandlimit erreicht? Temporär pausieren
        if (
                stripos($errorMsg, 'data not accepted') !== false ||   // generische PHPMailer-Msg
                stripos($errorMsg, '554 5.7.0')      !== false ||      // provider reply (falls vorhanden)
                stripos($errorMsg, 'limit')          !== false ||
                stripos($errorMsg, 'quota')          !== false ||
                stripos($errorMsg, 'rate')           !== false                               
            ) {
            // Status auf 'processing' setzen, damit Rotation später erneut versucht
            $db->prepare('UPDATE send_queue SET status = "processing", error_message = :msg WHERE id = :id')
                ->execute([':msg' => $errorMsg, ':id' => $entry['id']]);

            ErrorLogger::logSqlError('SendQueue | temporary send error | context: ' . json_encode(['queue_id' => $entry['id'], 'error' => $errorMsg], JSON_UNESCAPED_UNICODE));

        } else {
            // 🔴 Permanenter Fehler – nicht erneut versuchen
            $db->prepare('UPDATE send_queue SET status = "error", error_message = :msg WHERE id = :id')
                ->execute([':msg' => $errorMsg, ':id' => $entry['id']]);

            ErrorLogger::logSqlError('SendQueue | permanent send error | context: ' . json_encode(['queue_id' => $entry['id'], 'error' => $errorMsg], JSON_UNESCAPED_UNICODE));
        }
    }
    
}







