<?php

declare(strict_types=1);

/**
 * Bilder einer Veranstaltung einarbeiten (nur CLI).
 *
 *   php bin/veranstaltung-bilder.php <slug> <ordner> [-v]
 *
 * Erwarteter Ordnerinhalt (beides optional, mindestens eines nötig):
 *   <ordner>/plakate/*.{jpg|jpeg|png|webp}  Plakate, natsort = Reihenfolge.
 *                                           Das ERSTE ist das Hauptplakat und
 *                                           landet auf Karten und im OG-Bild.
 *   <ordner>/bilder/*.{jpg|…}               Fotos vom Fest, natsort = Reihenfolge
 *
 * DER SLUG MUSS SCHON IN data/veranstaltungen.json STEHEN. Umgekehrt zur
 * Stadionzeitung (dort legt das Skript die Ausgabe an und der Chat reichert sie
 * an) gilt hier: der Eintrag entsteht im Chat, das Skript reichert ihn an. Genau
 * daraus folgt die Garantie, dass chat-gepflegte Felder nie verloren gehen —
 * das Skript ändert am Eintrag ausschließlich `plakate` und `galerie` und lässt
 * alles andere unangetastet (auch `status`: nachgelieferte Fotos sind kein Grund,
 * eine veröffentlichte Seite offline zu nehmen — anders als bei
 * bin/stadionzeitung-add.php, wo ersetzte Seiten den Stand neu prüfbar machen).
 *
 * DER ORDNER IST IMMER DIE VOLLSTÄNDIGE GALERIE. Ein erneuter Lauf ersetzt sie
 * komplett (alte Dateien werden vorher gelöscht). Wer nur drei Fotos nachliefert,
 * verliert die anderen zwanzig — der Ordner muss also alles enthalten. Diese
 * Wahl ist Absicht: nur so lassen sich Fotos auch entfernen und umsortieren.
 *
 * Alt-Texte schreibt Claude im Chat, nicht dieses Skript. Damit sie einen
 * erneuten Lauf überleben, trägt jeder Galerie-Eintrag `source` (den
 * Originaldateinamen) — daran werden bestehende Alt-Texte wiedererkannt, auch
 * wenn sich die Reihenfolge durch ein neues Foto verschiebt. Neue Fotos bekommen
 * den Veranstaltungstitel als Platzhalter; das Skript nennt sie am Ende
 * namentlich, damit die Texte in derselben Chat-Runde entstehen.
 */
if (PHP_SAPI !== 'cli') {
    exit(1);
}

require dirname(__DIR__) . '/app/bootstrap.php';

$args = array_values(array_filter($argv, static fn (string $a): bool => $a !== '-v'));
$verbose = in_array('-v', $argv, true);
array_shift($args); // Skriptname

if (count($args) < 2) {
    fwrite(STDERR, "Nutzung: php bin/veranstaltung-bilder.php <slug> <ordner> [-v]\n");
    exit(1);
}

[$slug, $sourceDir] = $args;

$log = static function (string $msg, string $level = 'INFO') use ($verbose): void {
    log_write('veranstaltungen', $level, $msg);
    if ($verbose || $level === 'ERROR' || $level === 'WARN') {
        echo '[' . date('c') . "] {$level}  {$msg}\n";
    }
};

$fail = static function (string $msg) use ($log): never {
    $log("{$msg} — bestehende Daten bleiben unangetastet.", 'ERROR');
    exit(1);
};

// --- Eingaben validieren ---
if (!is_dir($sourceDir)) {
    $fail("Ordner nicht gefunden: {$sourceDir}");
}

$dataFile = DATA_PATH . '/veranstaltungen.json';
$data = is_file($dataFile)
    ? (json_decode((string) file_get_contents($dataFile), true) ?: ['veranstaltungen' => []])
    : ['veranstaltungen' => []];

$index = null;
foreach ($data['veranstaltungen'] ?? [] as $i => $entry) {
    if (($entry['slug'] ?? '') === $slug) {
        $index = $i;
        break;
    }
}

if ($index === null) {
    $known = implode(', ', array_column($data['veranstaltungen'] ?? [], 'slug'));
    $fail("Unbekannter Slug '{$slug}'. Der Eintrag muss zuerst in data/veranstaltungen.json angelegt werden. Bekannt: {$known}");
}

$veranstaltung = $data['veranstaltungen'][$index];
$titel = (string) ($veranstaltung['title'] ?? $slug);

// --- Quelldateien einsammeln ---
$exts = 'jpg,jpeg,png,webp,JPG,JPEG,PNG,WEBP';
$findAll = static function (string $subdir) use ($sourceDir, $exts): array {
    $dir = rtrim($sourceDir, '/') . "/{$subdir}";
    if (!is_dir($dir)) {
        return [];
    }
    $files = glob("{$dir}/*.{{$exts}}", GLOB_BRACE) ?: [];
    // natsort, damit IMG_9 vor IMG_10 kommt (strcmp würde umgekehrt sortieren).
    natsort($files);
    return array_values($files);
};

$plakatSources = $findAll('plakate');
$fotoSources = $findAll('bilder');

if ($plakatSources === [] && $fotoSources === []) {
    $fail("Weder plakate/* noch bilder/* in {$sourceDir} gefunden.");
}

// --- Lock gegen parallele Läufe ---
if (!is_dir(STORAGE_PATH . '/cache')) {
    mkdir(STORAGE_PATH . '/cache', 0755, true);
}
$lock = fopen(STORAGE_PATH . '/cache/veranstaltungen.lock', 'c');
if (!$lock || !flock($lock, LOCK_EX | LOCK_NB)) {
    $log('Läuft bereits (Lock belegt) — Lauf übersprungen.', 'WARN');
    exit(1);
}

// --- Bestehende Alt-Texte sichern, bevor irgendetwas ersetzt wird ---
$altBySource = [];
foreach (array_merge($veranstaltung['plakate'] ?? [], $veranstaltung['galerie'] ?? []) as $bild) {
    if (!empty($bild['source']) && !empty($bild['alt'])) {
        $altBySource[(string) $bild['source']] = (string) $bild['alt'];
    }
}

// --- Ziel-Ordner vorbereiten: bei erneutem Lauf alte Dateien wegräumen ---
$destDir = PUBLIC_PATH . "/assets/img/veranstaltungen/{$slug}";
if (!is_dir($destDir)) {
    mkdir($destDir, 0755, true);
} else {
    foreach (array_merge(
        glob("{$destDir}/plakat-*.jpg") ?: [],
        glob("{$destDir}/bild-*.jpg") ?: []
    ) as $old) {
        unlink($old);
    }
}

// Plakate brauchen die große Fassung: sie tragen Text, und in der Großansicht
// will man Uhrzeiten lesen können. Fotos nicht — dort ist die Großansicht nur
// „größer ansehen", und die dritte Stufe kostet bei 40 Fotos mehr Platz als sie
// bringt (gemessen: über die Hälfte des gesamten Bildvolumens).
$plakatWidths = [640, 1000, 1600];
$fotoWidths = [640, 1200];

/**
 * Eine Quelldatei in die responsiven Breiten rendern und den Bild-Eintrag für
 * die JSON bauen. Die `widths` im Eintrag sind die TATSÄCHLICH erzeugten —
 * resize_image_variants() skaliert nie hoch, eine kleine Quelle liefert also
 * weniger Varianten und img.php bleibt ehrlich.
 */
$render = static function (string $src, string $destBase, string $relBase, string $alt, int $quality, array $widths) use ($fail): array {
    try {
        $variants = resize_image_variants($src, $destBase, $widths, $quality);
    } catch (RuntimeException $e) {
        $fail('Bild nicht verarbeitbar (' . basename($src) . '): ' . $e->getMessage());
    }
    $generated = array_values(array_unique(array_column($variants, 'width')));
    sort($generated);

    return ['base' => $relBase, 'widths' => $generated, 'alt' => $alt];
};

$ohneAlt = [];

/**
 * Eine Liste von Quelldateien zu Bild-Einträgen rendern. $prefix bestimmt den
 * Dateinamen (plakat-01…, bild-01…), $quality die JPEG-Qualität: Plakate tragen
 * Text, da ist Ringing um Buchstaben sichtbar — Fotos brauchen das nicht.
 */
$renderAlle = static function (array $sources, string $prefix, int $quality, array $widths, string $fallbackAlt)
    use ($render, $destDir, $slug, $altBySource, &$ohneAlt): array {
    $eintraege = [];
    foreach ($sources as $i => $src) {
        $source = basename($src);
        $nummer = str_pad((string) ($i + 1), 2, '0', STR_PAD_LEFT);
        $alt = $altBySource[$source] ?? '';
        if ($alt === '') {
            $ohneAlt[] = $source;
        }

        $eintrag = $render(
            $src,
            "{$destDir}/{$prefix}-{$nummer}",
            "img/veranstaltungen/{$slug}/{$prefix}-{$nummer}",
            $alt !== '' ? $alt : $fallbackAlt,
            $quality,
            $widths
        );
        $eintrag['source'] = $source;
        $eintraege[] = $eintrag;
    }
    return $eintraege;
};

$plakate = $renderAlle($plakatSources, 'plakat', 82, $plakatWidths, "Plakat zum {$titel}");
$galerie = $renderAlle($fotoSources, 'bild', 78, $fotoWidths, $titel);

// --- Nur plakate + galerie ersetzen, alles andere bleibt, wie es ist ---
$data['veranstaltungen'][$index]['plakate'] = $plakate;
$data['veranstaltungen'][$index]['galerie'] = $galerie;

$payload = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$tmp = $dataFile . '.tmp';
file_put_contents($tmp, $payload);
if (!rename($tmp, $dataFile)) {
    $fail('Konnte data/veranstaltungen.json nicht schreiben.');
}

$log(sprintf(
    '%s: Bilder komplett ersetzt (%d Plakate, %d Fotos).',
    $slug,
    count($plakate),
    count($galerie)
));

echo "Veranstaltung {$slug}: Bilder komplett ersetzt, jetzt "
    . count($plakate) . " Plakat(e) und " . count($galerie) . " Foto(s).\n";

if ($ohneAlt !== []) {
    echo count($ohneAlt) . " Bild(er) noch ohne Alt-Text: " . implode(', ', $ohneAlt) . "\n";
    echo "Alt-Texte in data/veranstaltungen.json nachtragen (Wir-Form, siehe CLAUDE.md).\n";
}
