<?php
// formular_submit.php

require_once __DIR__ . '/includes/init.php';
require_once __DIR__ . '/includes/core/FormularLogger.php';
require_once __DIR__ . '/includes/core/ListSignupNotificationMailer.php';
require_once __DIR__ . '/includes/core/SodiumCrypto.php';
require_once __DIR__ . '/includes/functions.php';

// Nur POST-Anfragen zulassen
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    exit('Method Not Allowed');
}

// -----------------------------------------------------------
// 0. Konfiguration laden (enthaelt DOI-Key & HASH-Salt)
// -----------------------------------------------------------
$config    = require __DIR__ . '/includes/config.php';
$hashSalt  = $config['security']['hash_salt'] ?? '';
$cryptoKey = $config['security']['doi_key']   ?? '';

// -----------------------------------------------------------
// 1. Nur POST zulassen (doppelte Absicherung - bleibt wie im Original)
// -----------------------------------------------------------
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    exit('Method Not Allowed');
}

/**
 * -----------------------------------------------------------
 * Sperrliste-Check (Intake-only!)
 * -----------------------------------------------------------
 * Diese Funktion prueft ausschliesslich beim "Reinkommen" (Formular Submit),
 * ob die E-Mail oder deren Domain in der suppression_list aktiv gesperrt ist.
 *
 * WICHTIG: Nach deiner Anforderung wird NICHT an anderer Stelle geprueft
 * (keine Queue/Versandpruefung).
 *
 * Rueckgabe:
 * - false => nicht gesperrt
 * - array => gesperrt (enthaelt reason/type/value)
 */
function checkSuppressionList(PDO $db, string $email)
{
    $email = trim(strtolower($email));
    if ($email === '' || strpos($email, '@') === false) {
        return false;
    }

    $domain = substr(strrchr($email, "@"), 1);
    $domain = trim(strtolower($domain));
    if ($domain === '') {
        return false;
    }

    try {
        // Hinweis:
        // - is_active = 1 => aktiv
        // - expires_at NULL oder > NOW() => nicht abgelaufen
        // - Pruefen auf exakte E-Mail oder Domain
        $sql = "
            SELECT id, type, value, reason, details, expires_at
            FROM suppression_list
            WHERE is_active = 1
              AND (expires_at IS NULL OR expires_at > NOW())
              AND (
                   (type = 'email'  AND value = :email)
                OR (type = 'domain' AND value = :domain)
              )
            ORDER BY CASE WHEN type = 'email' THEN 0 ELSE 1 END
            LIMIT 1
        ";

        $stmt = $db->prepare($sql);
        $stmt->execute([
            ':email'  => $email,
            ':domain' => $domain,
        ]);

        $hit = $stmt->fetch(PDO::FETCH_ASSOC);

        ErrorLogger::logSqlSuccess('formular_submit: suppression check intake | context: ' . json_encode([
            'email'   => $email,
            'domain'  => $domain,
            'blocked' => (bool)$hit
        ], JSON_UNESCAPED_UNICODE));

        return $hit ?: false;

    } catch (Throwable $e) {
        // Fail-open: Wenn die Sperrliste nicht geprueft werden kann,
        // blocken wir NICHT, damit der Formularflow nicht kaputt geht.
        ErrorLogger::logSqlError('Sperrliste-Check (formular_submit) fehlgeschlagen: ' . $e->getMessage());
        return false;
    }
}

$isInternal = ($_POST['_admin_preview'] ?? '') === 'vorschau_admin_token';
$formId = (int) ($_POST['form_id'] ?? 0);

$email = trim($_POST['email'] ?? '');
$checkEmail = trim($_POST['check_email'] ?? '');
$ip = anonymizeIp($_SERVER['REMOTE_ADDR'] ?? 'unknown');

$emailRaw     = trim($_POST['email'] ?? '');
$ipOriginal   = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
$ipMasked     = anonymizeIp($ipOriginal); // Fuer gewoehnliches Log

if (!$isInternal && (!$formId || !$email || $checkEmail)) {
    FormularLogger::log($formId, $email, $ip, FormularLogger::ACTION_VALIDATION_ERROR, 'Ungueltige Eingabe oder Spam-Verdacht', false);
    exit('Ein Fehler ist aufgetreten. Bitte versuche es spaeter erneut');
}

//------------------------------------------------------------
//  DOI-spezifische Pseudonymisierung (enc + Hash)
//------------------------------------------------------------
try {
    $ipCipher = SodiumCrypto::encrypt($ipOriginal, $cryptoKey);   // VARBINARY(255)
    $ipHash   = hash_hmac('sha256', $ipOriginal, $hashSalt);      // CHAR(64)
} catch (Throwable $e) {
    // Fallback: notfalls nur Hash speichern
    ErrorLogger::logSqlError('IP - Verschluesselung fehlgeschlagen: ' . $e->getMessage());

    $ipCipher = null;
    $ipHash   = hash_hmac('sha256', $ipOriginal, $hashSalt);
}

$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? null;
$referrer  = $_SERVER['HTTP_REFERER']      ?? null;
$email     = filter_var($emailRaw, FILTER_VALIDATE_EMAIL) ? strtolower($emailRaw) : '';

try {
    $db = Database::getInstance();

    // -----------------------------------------------------------
    // 2) Formular & Listendaten laden (nur extern)
    // -----------------------------------------------------------
    if (!$isInternal) {
        // Formular und zugehoerige Haupt-Liste laden.
        // Bevorzugt wird die aktuelle Zuordnung ueber form_list, Fallback bleibt forms.list_id.
        $stmt = $db->prepare("
            SELECT
                f.*,
                COALESCE(fl_primary.list_id, f.list_id) AS resolved_list_id,
                l.id AS list_id,
                l.name AS list_name,
                l.requires_doi,
                l.doi_template_id,
                l.from_identity_id,
                l.welcome_template_id,
                l.notify_on_signup
            FROM forms f
            LEFT JOIN (
                SELECT fl.form_id, MIN(fl.list_id) AS list_id
                FROM form_list fl
                GROUP BY fl.form_id
            ) fl_primary ON fl_primary.form_id = f.id
            LEFT JOIN lists l ON l.id = COALESCE(fl_primary.list_id, f.list_id)
            WHERE f.id = ?
            LIMIT 1
        ");
        $stmt->execute([$formId]);
        $form = $stmt->fetch(PDO::FETCH_ASSOC);

        if (!$form || empty($form['resolved_list_id']) || empty($form['list_id'])) {
            ErrorLogger::logSqlError("Formular $formId nicht gefunden");
            FormularLogger::log($formId, $email, $ip, FormularLogger::ACTION_VALIDATION_ERROR, 'Formular nicht gefunden', false);
            exit('Ein Fehler ist aufgetreten. Bitte versuche es spaeter erneut');
        }

        ErrorLogger::logSqlSuccess("Formulardaten geladen fuer Formular $formId");

        $anrede   = $form['show_anrede']   ? ($_POST['anrede'] ?? '') : null;
        $vorname  = $form['show_vorname']  ? trim($_POST['vorname'] ?? '') : null;
        $nachname = $form['show_nachname'] ? trim($_POST['nachname'] ?? '') : null;

        if ($form['require_vorname'] && !$vorname) {
            FormularLogger::log($formId, $email, $ip, FormularLogger::ACTION_VALIDATION_ERROR, 'Vorname fehlt', false);
            exit('Bitte gib deinen Vornamen an.');
        }
        if ($form['require_nachname'] && !$nachname) {
            FormularLogger::log($formId, $email, $ip, FormularLogger::ACTION_VALIDATION_ERROR, 'Nachname fehlt', false);
            exit('Bitte gib deinen Nachnamen an.');
        }
        if ($vorname && !preg_match('/^[\p{L}]+$/u', $vorname)) {
            FormularLogger::log($formId, $email, $ip, FormularLogger::ACTION_INVALID_EMAIL, 'Ungueltiger Vorname', false);
            exit('Bitte gib einen gÃƒÆ’Ã†â€™Ãƒâ€šÃ‚Â¼ltigen Vornamen ein.');
        }
        if ($nachname && !preg_match('/^[\p{L}]+$/u', $nachname)) {
            FormularLogger::log($formId, $email, $ip, FormularLogger::ACTION_INVALID_EMAIL, 'Ungueltiger Nachname', false);
            exit('Bitte gib einen gÃƒÆ’Ã†â€™Ãƒâ€šÃ‚Â¼ltigen Nachnamen ein.');
        }
    }

    // -----------------------------------------------------------
    // 3) Sperrliste anwenden (Intake-only!)
    // -----------------------------------------------------------
    // Deine Vorgabe: Pruefung nur dort, wo Adressen "reinkommen".
    // -> Formular submit ist einer dieser Punkte.
    // Interne Vorschau/Preview soll nicht blockiert werden.
    if (!$isInternal) {
        $suppressed = checkSuppressionList($db, $email);

        if ($suppressed !== false) {
            // Intern ausfuehrlich loggen - aber dem User keine Details verraten.
            ErrorLogger::logSqlSuccess('formular_submit: suppression blocked entry | context: ' . json_encode([
                'form_id' => $formId,
                'email'   => $email,
                'type'    => $suppressed['type'] ?? null,
                'reason'  => $suppressed['reason'] ?? null,
                'value'   => $suppressed['value'] ?? null,
            ], JSON_UNESCAPED_UNICODE));

            FormularLogger::log($formId, $email, $ip, FormularLogger::ACTION_VALIDATION_ERROR, 'Sperrliste blockiert Eintrag', false);

            // Neutrale Ausgabe (keine Info, ob Sperrliste existiert)
            exit('Ein Fehler ist aufgetreten. Bitte versuche es spaeter erneut');
        }
    }

    // -----------------------------------------------------------
    // 4) Empfaenger pruefen oder anlegen
    // -----------------------------------------------------------
    $stmt = $db->prepare("SELECT id FROM recipients WHERE email = ?");
    $stmt->execute([$email]);
    $recipientId = $stmt->fetchColumn();
    ErrorLogger::logSqlSuccess("Empfaengerpruefung fuer $email durchgefuehrt");

    if (!$recipientId) {
        $stmt = $db->prepare("INSERT INTO recipients (email, anrede, vorname, nachname,  created_at) VALUES (?, ?, ?, ?,  NOW())");
        $stmt->execute([$email, $anrede ?? null, $vorname ?? null, $nachname ?? null]);
        $recipientId = $db->lastInsertId();
        ErrorLogger::logSqlSuccess("Neuer Empfaenger $email angelegt mit ID $recipientId");
    } else {
        ErrorLogger::logSqlSuccess("Empfaenger $email schon vorhanden");
    }

    $submittedCustomFields = [];

    // Benutzerdefinierte Felder speichern
    if (!$isInternal) {
        $stmt = $db->prepare("SELECT cfd.field_key FROM form_fields ff JOIN custom_field_definitions cfd ON ff.field_id = cfd.id WHERE ff.form_id = ?");
        $stmt->execute([$formId]);
        $felder = $stmt->fetchAll();
        ErrorLogger::logSqlSuccess("Custom Fields geladen fuer Formular $formId");

        foreach ($felder as $feld) {
            $key = $feld['field_key'];
            if (isset($_POST[$key])) {
                $value = is_array($_POST[$key]) ? implode(',', $_POST[$key]) : trim($_POST[$key]);
                $insert = $db->prepare("INSERT INTO recipient_custom_fields (recipient_id, field_key, field_value) VALUES (?, ?, ?)");
                $submittedCustomFields[$key] = $value;
                $insert->execute([$recipientId, $key, $value]);
                ErrorLogger::logSqlSuccess("Custom Field $key gespeichert fuer Empfaenger $recipientId");
            }
        }
    }

    // Empfaenger der Liste zuordnen
    $stmt = $db->prepare("SELECT COUNT(*) FROM recipient_lists WHERE recipient_id = ? AND list_id = ?");
    $stmt->execute([$recipientId, $form['list_id']]);
    if ($stmt->fetchColumn() == 0) {
        $pauseToken = bin2hex(random_bytes(32));
        $stmt = $db->prepare("INSERT INTO recipient_lists (recipient_id, list_id, subscribed_at, pause_token) VALUES (?, ?, NOW(), ?)");
        $stmt->execute([$recipientId, $form['list_id'], $pauseToken]);
        ErrorLogger::logSqlSuccess("Empfaenger $recipientId zur Liste {$form['list_id']} hinzugefuegt");
    }

    // Formular-Tags dem Empfaenger zuweisen
    try {
        $stmt = $db->prepare("SELECT tag_id FROM form_tags WHERE form_id = ?");
        $stmt->execute([$formId]);
        $formTagIds = $stmt->fetchAll(PDO::FETCH_COLUMN);

        if (!empty($formTagIds)) {
            $insertStmt = $db->prepare("INSERT IGNORE INTO recipient_tags (recipient_id, tag_id, assigned_at) VALUES (?, ?, NOW())");

            foreach ($formTagIds as $tagId) {
                $insertStmt->execute([$recipientId, $tagId]);
            }

            ErrorLogger::logSqlSuccess("Tags aus Formular $formId an Empfaenger $recipientId uebertragen.");
        }
    } catch (Exception $e) {
        ErrorLogger::logSqlError("Fehler bei Tag-Zuweisung: " . $e->getMessage());
    }
    if (!$isInternal) {
        sendListSignupNotification(
            $db,
            $form,
            [
                'recipient_id' => (int) $recipientId,
                'email' => $email,
                'anrede' => $anrede ?? '',
                'vorname' => $vorname ?? '',
                'nachname' => $nachname ?? '',
            ],
            [
                'submitted_at' => date('Y-m-d H:i:s'),
                'anonymized_ip' => $ipMasked,
                'referrer' => $referrer,
                'submitted_fields' => [
                    'E-Mail' => $email,
                    'Anrede' => $anrede ?? '',
                    'Vorname' => $vorname ?? '',
                    'Nachname' => $nachname ?? '',
                ],
                'custom_fields' => $submittedCustomFields,
            ]
        );
    }


    //--------------------------------------------------------
    //  Opt-In Datensatz anlegen (NEU: volle DOI-Daten)
    //--------------------------------------------------------
    if (!$isInternal) {
        $token = bin2hex(random_bytes(16));

        $sql = '
            INSERT INTO recipient_list_optin (
                recipient_id, list_id, doi_token, doi_confirmed,
                doi_requested_at, formular_id,
                doi_ip_signup_enc, doi_ip_signup_hash,
                doi_user_agent, doi_referrer
            ) VALUES (
                :rid, :lid, :token, 0,
                NOW(), :fid,
                :ip_enc, :ip_hash,
                :ua, :ref
            )
            ON DUPLICATE KEY UPDATE
                doi_token          = VALUES(doi_token),
                doi_requested_at   = NOW(),
                doi_ip_signup_enc  = VALUES(doi_ip_signup_enc),
                doi_ip_signup_hash = VALUES(doi_ip_signup_hash),
                doi_user_agent     = VALUES(doi_user_agent),
                doi_referrer       = VALUES(doi_referrer),
                doi_confirmed      = 0
        ';

        $stmt = $db->prepare($sql);
        $stmt->execute([
            ':rid'     => $recipientId,
            ':lid'     => $form['list_id'],
            ':token'   => $token,
            ':fid'     => $formId,
            ':ip_enc'  => $ipCipher,
            ':ip_hash' => $ipHash,
            ':ua'      => $userAgent,
            ':ref'     => $referrer,
        ]);

        ErrorLogger::logSqlSuccess('formular_submit: create DOI record | SQL: ' . $stmt->queryString . ' | params: ' . json_encode(['recipient_id' => $recipientId, 'list_id' => $form['list_id']], JSON_UNESCAPED_UNICODE));
    }

    //--------------------------------------------------------
    //  DOI- oder Welcome-Mail in Queue einplanen
    //--------------------------------------------------------
    if (!$isInternal) {
        $viewToken = $token;
        $priority  = 1;

        $queueStmt = $db->prepare(
            'INSERT INTO send_queue (campaign_id, recipient_id, template_id, view_token, priority, scheduled_at, status, created_at) VALUES (NULL, :rid, :tid, :vt, :prio, NOW(), "pending", NOW())'
        );

        if (!empty($form['requires_doi'])) {
            if (empty($form['doi_template_id'])) {
                ErrorLogger::logSqlError('formular_submit: DOI aktiv, aber kein DOI-Template gesetzt | context: ' . json_encode([
                    'form_id' => $formId,
                    'list_id' => $form['list_id'],
                    'recipient_id' => $recipientId,
                ], JSON_UNESCAPED_UNICODE));
                throw new RuntimeException('Double-Opt-In ist fuer diese Liste aktiv, aber es ist kein DOI-Template hinterlegt.');
            }

            $queueStmt->execute([
                ':rid'  => $recipientId,
                ':tid'  => $form['doi_template_id'],
                ':vt'   => $viewToken,
                ':prio' => $priority,
            ]);
            ErrorLogger::logSqlSuccess('formular_submit: enqueue DOI mail | SQL: ' . $queueStmt->queryString . ' | params: ' . json_encode(['recipient_id' => $recipientId, 'template_id' => $form['doi_template_id']], JSON_UNESCAPED_UNICODE));

            //------------------------------------------------------------
            // 2. Stufe - Template-Body laden & Opt-In updaten
            //------------------------------------------------------------
            try {
                $tmplStmt = $db->prepare(
                    'SELECT body_html
                    FROM email_templates
                    WHERE id = :tid
                    LIMIT 1'
                );
                $tmplStmt->execute([':tid' => $form['doi_template_id']]);
                $bodyHtml = $tmplStmt->fetchColumn();

                if (!$bodyHtml) {
                    throw new RuntimeException('Template-Body leer oder Template nicht gefunden');
                }

                $mailHash    = hash('sha256', $bodyHtml);
                $mailBodyB64 = base64_encode($bodyHtml);

                $upd = $db->prepare(
                    'UPDATE recipient_list_optin
                        SET doi_mail_sent_at   = NOW(),
                            doi_mail_hash      = :mhash,
                            doi_mail_body_b64  = :mb64
                    WHERE recipient_id = :rid
                        AND list_id      = :lid'
                );
                $upd->execute([
                    ':mhash' => $mailHash,
                    ':mb64'  => $mailBodyB64,
                    ':rid'   => $recipientId,
                    ':lid'   => $form['list_id'],
                ]);

                ErrorLogger::logSqlSuccess('formular_submit: store DOI mail body/hash | context: ' . json_encode(['recipient_id' => $recipientId, 'list_id' => $form['list_id']], JSON_UNESCAPED_UNICODE));

            } catch (Throwable $e) {
                ErrorLogger::logSqlError('formular_submit: store DOI mail body/hash failed | error: ' . $e->getMessage());

                echo '<div class="alert alert-danger alert-dismissible fade show" role="alert">
                        <i class="tml tml-exclamation-circle"></i>
                        DOI-E-Mail konnte nicht protokolliert werden.
                        <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
                    </div>
                    <script>setTimeout(()=>document.querySelector(".alert")?.remove(),5000);</script>';
                exit;
            }

        } elseif (!empty($form['welcome_template_id'])) {
            $stmt = $db->prepare("INSERT INTO send_queue (campaign_id, recipient_id, template_id, view_token, priority, scheduled_at, status, created_at) VALUES (?, ?, ?, ?, ?, NOW(), 'pending', NOW())");
            $stmt->execute([null, $recipientId, $form['welcome_template_id'], $viewToken, $priority]);
            ErrorLogger::logSqlSuccess("Willkommensmail eingeplant fuer Empfaenger $recipientId");
        }
    }

    // Warteschlange abarbeiten
    include __DIR__ . '/cron/send_queue_worker_s.php';

    FormularLogger::log($formId, $email, $ip, FormularLogger::ACTION_SUCCESS, 'Eintrag erfolgreich verarbeitet', $isInternal);

    // Weiterleitung oder Erfolgstext
    if (!$isInternal && !empty($form['redirect_url'])) {
        header('Location: ' . $form['redirect_url']);
        exit;
    }

    if (!$isInternal) {
        $successText = trim((string) ($form['success_message'] ?? ''));
        if ($successText === '') {
            $successText = trim((string) ($form['erfolgstext'] ?? ''));
        }
        if ($successText === '') {
            $successText = 'Vielen Dank fuer deine Anmeldung!';
        }

        echo htmlspecialchars($successText);
    }

} catch (Exception $e) {
    ErrorLogger::logSqlError("Fehler beim Submit von Formular $formId: " . $e->getMessage());
    FormularLogger::log($formId, $email, $ip, FormularLogger::ACTION_VALIDATION_ERROR, $e->getMessage(), $isInternal);
    exit('Ein Fehler ist aufgetreten. Bitte versuche es spaeter erneut.');
}
