<?php

declare(strict_types=1);

/**
 * POST /mitglied-werden-senden — Mitgliedsantrag Fußball (schlank, ohne Bankdaten).
 * Versendet eine interne Vereins-Mail (erfolgskritisch) und eine Eingangsbestätigung
 * an den Antragsteller (optional, Fehler dort wird nicht an den Besucher geleakt).
 * Antwort: PRG-Redirect zu /mitglied-werden#anmeldung-fussball (?sent=1 | ?error=…),
 *          JSON bei fetch (form.js) oder — bei Feldfehlern ohne JS — die Seite direkt
 *          mit HTTP 422, erhaltenen Eingaben und Feldfehlern (render_form_invalid()).
 */

$wantsJson = str_contains($_SERVER['HTTP_ACCEPT'] ?? '', 'application/json');

$respond = static function (bool $ok, string $error = '') use ($wantsJson): never {
    if ($wantsJson) {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode(['ok' => $ok, 'error' => $error ?: null]);
    } else {
        header('Location: ' . url('mitglied-werden') . ($ok ? '?sent=1' : '?error=' . $error) . '#anmeldung-fussball', true, 303);
    }
    exit;
};

$field = static fn (string $key): string => trim((string) ($_POST[$key] ?? ''));

$route = 'mitglied-werden';
$token = $field('ft');

// Ohne vollständige config/config.php liefe die Seite auf der Vorlage (öffentliches
// app_secret, kein SMTP-Passwort) — dann gar nicht erst verarbeiten.
if (!config_ready($route)) {
    $respond(false, 'mail');
}

// --- Spam-Checks: Bots bekommen ein stilles "OK" (kein Feedback-Kanal) ---
if ($field('company_url') !== '') {
    log_spam($route, 'honeypot');
    $respond(true);
}
if (!form_token_valid($token)) {
    log_spam($route, 'token');
    $respond(true);
}

// --- Eingaben ---
$firstName = $field('first_name');
$lastName = $field('last_name');
$birthDate = $field('birth_date');
$phone = $field('phone');
$street = $field('street');
$zip = $field('zip');
$city = $field('city');
$email = $field('email');
$feeClass = $field('fee_class');
$statute = ($_POST['statute'] ?? '') === '1';
$privacy = ($_POST['privacy'] ?? '') === '1';

// Beitragsklassen-Whitelist identisch zur Seite aus data/mitglied-werden.json ableiten.
$data = json_load('mitglied-werden');
$feeOptions = array_map(
    static fn (array $r): string => $r['label'] . ' — ' . $r['price'],
    $data['fussball']['fees']['rows'] ?? []
);

// Geburtsdatum: Format, echter Kalendertag und plausibler Zeitraum.
$birthValid = false;
if (preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $birthDate, $m) === 1) {
    $birthValid = checkdate((int) $m[2], (int) $m[3], (int) $m[1])
        && $birthDate <= date('Y-m-d')
        && (int) $m[1] >= (int) date('Y') - 120;
}

// --- Validierung (Whitelist-Prinzip, Fehler pro Feld) ---
$errors = [];
if ($firstName === '' || mb_strlen($firstName) > 100 || preg_match('/[\r\n]/', $firstName)) {
    $errors['mm-first-name'] = 'Bitte gib den Vornamen an (max. 100 Zeichen).';
}
if ($lastName === '' || mb_strlen($lastName) > 100 || preg_match('/[\r\n]/', $lastName)) {
    $errors['mm-last-name'] = 'Bitte gib den Nachnamen an (max. 100 Zeichen).';
}
if (!$birthValid) {
    $errors['mm-birth-date'] = 'Bitte gib ein gültiges Geburtsdatum an.';
}
if ($phone === '' || mb_strlen($phone) > 50) {
    $errors['mm-phone'] = 'Bitte gib eine Telefonnummer an (max. 50 Zeichen).';
}
if ($street === '' || mb_strlen($street) > 200 || preg_match('/[\r\n]/', $street)) {
    $errors['mm-street'] = 'Bitte gib Straße und Hausnummer an.';
}
if ($zip === '' || mb_strlen($zip) > 10 || preg_match('/[^\p{L}\d \-]/u', $zip)) {
    $errors['mm-zip'] = 'Bitte gib eine gültige Postleitzahl an.';
}
if ($city === '' || mb_strlen($city) > 100 || preg_match('/[\r\n]/', $city)) {
    $errors['mm-city'] = 'Bitte gib den Wohnort an (max. 100 Zeichen).';
}
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
    $errors['mm-email'] = 'Bitte gib eine gültige E-Mail-Adresse an.';
}
if (!in_array($feeClass, $feeOptions, true)) {
    $errors['mm-fee-class'] = 'Bitte wähle eine Beitragsklasse aus der Liste.';
}
if (!$statute) {
    $errors['mm-statute'] = 'Ohne Anerkennung der Satzung ist keine Mitgliedschaft möglich.';
}
if (!$privacy) {
    $errors['mm-privacy'] = 'Ohne Zustimmung zum Datenschutz können wir den Antrag nicht entgegennehmen.';
}

if ($errors !== []) {
    if ($wantsJson) {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode(['ok' => false, 'error' => 'validation', 'fields' => $errors]);
        exit;
    }
    // Ohne JS: Seite mit Eingaben und Feldfehlern neu rendern statt PRG (Werte bleiben erhalten).
    render_form_invalid($route, [
        'first_name' => $firstName, 'last_name' => $lastName, 'birth_date' => $birthDate,
        'phone' => $phone, 'street' => $street, 'zip' => $zip, 'city' => $city,
        'email' => $email, 'fee_class' => $feeClass,
        'statute' => $statute ? '1' : '', 'privacy' => $privacy ? '1' : '',
    ], $errors);
}

// --- Volumen-Grenzen: erst ab hier, damit Tippfehler kein Kontingent verbrauchen.
// Sie antworten ehrlich — stilles "Danke" ohne Versand verschluckt echte Anträge. ---
if (!rate_limit_ok('submit|' . $route . '|' . client_ip(), 5, 600)) {
    log_spam($route, 'ratelimit');
    $respond(false, 'ratelimit');
}
// Token-Mehrfachnutzung begrenzen (Replay-Schutz). IP im Schlüssel, weil form_token()
// sekundengenau ist: sonst teilen sich alle Besucher derselben Sekunde ein Kontingent.
if (!rate_limit_ok('token|' . $token . '|' . client_ip(), 3, 86400)) {
    log_spam($route, 'replay');
    $respond(false, 'ratelimit');
}
// Globaler Tages-Cap über alle Formulare: schützt Postfach und Absender-Reputation.
if (!rate_limit_ok('daily', 200, 86400)) {
    log_spam($route, 'daily-cap');
    $respond(false, 'busy');
}

// --- Versand ---
$body = "Neuer Mitgliedsantrag (Fußball) über tsv08kulmbach.de\n"
    . str_repeat('-', 40) . "\n"
    . "Vorname:        {$firstName}\n"
    . "Nachname:       {$lastName}\n"
    . "Geboren am:     {$birthDate}\n"
    . "Adresse:        {$street}, {$zip} {$city}\n"
    . "Telefon:        {$phone}\n"
    . "E-Mail:         {$email}\n"
    . "Beitragsklasse: {$feeClass}\n"
    . str_repeat('-', 40) . "\n"
    . "Satzung anerkannt:  ja\n"
    . "Datenschutz:        ja\n"
    . 'Eingegangen:        ' . date('d.m.Y H:i') . "\n\n"
    . "Hinweis: Bankdaten werden online nicht erhoben. Beitrittserklärung\n"
    . "per Post zur Unterschrift zusenden.\n";

// Mail 1 (intern) — erfolgskritisch.
$sent = send_mail([
    'context'  => 'Mitgliedsantrag',
    'subject'  => 'Mitgliedsantrag Fußball: ' . $firstName . ' ' . $lastName,
    'body'     => $body,
    'reply_to' => ['email' => $email, 'name' => $firstName . ' ' . $lastName],
]);

if (!$sent) {
    $respond(false, 'mail');
}

// Mail 2 (Eingangsbestätigung an den Antragsteller) — optional, Fehler nur ins Log.
$welcome = $data['welcome_mail'] ?? null;
if (is_array($welcome) && !empty($welcome['subject']) && !empty($welcome['body'])) {
    send_mail([
        'context' => 'Bestätigungsmail Mitgliedsantrag',
        'to'      => $email,
        'to_name' => $firstName . ' ' . $lastName,
        'subject' => (string) $welcome['subject'],
        'body'    => (string) $welcome['body'],
    ]);
}

$respond(true);
