<?php

if (!function_exists('impfTableExists')) {
    function impfTableExists(PDO $pdo, string $table): bool
    {
        $st = $pdo->prepare("SHOW TABLES LIKE :table");
        $st->execute(['table' => $table]);
        return (bool)$st->fetchColumn();
    }
}

if (!function_exists('impfTableHasColumn')) {
    function impfTableHasColumn(PDO $pdo, string $table, string $column): bool
    {
        static $cache = [];

        $cacheKey = $table . '.' . $column;
        if (array_key_exists($cacheKey, $cache)) {
            return $cache[$cacheKey];
        }

        $st = $pdo->prepare("SHOW COLUMNS FROM `" . $table . "` LIKE :column");
        $st->execute(['column' => $column]);
        $cache[$cacheKey] = (bool)$st->fetch(PDO::FETCH_ASSOC);
        return $cache[$cacheKey];
    }
}

if (!function_exists('impfTableHasIndex')) {
    function impfTableHasIndex(PDO $pdo, string $table, string $index): bool
    {
        $st = $pdo->prepare("SHOW INDEX FROM `" . $table . "` WHERE Key_name = :index_name");
        $st->execute(['index_name' => $index]);
        return (bool)$st->fetch(PDO::FETCH_ASSOC);
    }
}

if (!function_exists('impfEnsureTable')) {
    function impfEnsureTable(PDO $pdo, string $table, string $createSql): void
    {
        if (impfTableExists($pdo, $table)) {
            return;
        }

        $pdo->exec($createSql);
    }
}

if (!function_exists('impfNormalizeZeitraumIds')) {
    function impfNormalizeZeitraumIds($zeitraumIds): array
    {
        if ($zeitraumIds === null) {
            return [];
        }

        if (!is_array($zeitraumIds)) {
            $zeitraumIds = [$zeitraumIds];
        }

        $result = [];
        foreach ($zeitraumIds as $zeitraumId) {
            $zeitraumId = (int)$zeitraumId;
            if ($zeitraumId <= 0 || isset($result[$zeitraumId])) {
                continue;
            }
            $result[$zeitraumId] = $zeitraumId;
        }

        return array_values($result);
    }
}

if (!function_exists('impfWeekdayName')) {
    function impfWeekdayName(int $day): string
    {
        $map = [
            1 => 'Montag',
            2 => 'Dienstag',
            3 => 'Mittwoch',
            4 => 'Donnerstag',
            5 => 'Freitag',
            6 => 'Samstag',
            7 => 'Sonntag',
        ];

        return $map[$day] ?? 'Unbekannt';
    }
}

if (!function_exists('impfWorkflowEnsureTables')) {
    function impfWorkflowEnsureTables(PDO $pdo): void
    {
        impfEnsureTable($pdo, 'impf_workflow_meta', "CREATE TABLE impf_workflow_meta (
            meta_key VARCHAR(100) NOT NULL,
            meta_value VARCHAR(255) NOT NULL DEFAULT '',
            updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            PRIMARY KEY (meta_key)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3");

        impfEnsureTable($pdo, 'impfstoff_workflow', "CREATE TABLE impfstoff_workflow (
            impfstoff_id INT NOT NULL,
            dosen_pro_flasche INT NOT NULL,
            updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            PRIMARY KEY (impfstoff_id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3");

        impfEnsureTable($pdo, 'impfstoff_wochenplan', "CREATE TABLE impfstoff_wochenplan (
            plan_id INT NOT NULL AUTO_INCREMENT,
            impfstoff_id INT NOT NULL,
            wochentag TINYINT NOT NULL,
            start TIME NOT NULL,
            ende TIME NOT NULL,
            impfortid INT NOT NULL,
            aktiv TINYINT NOT NULL DEFAULT 1,
            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (plan_id),
            INDEX idx_impfstoff_wochenplan_impfstoff (impfstoff_id),
            INDEX idx_impfstoff_wochenplan_wochentag (wochentag)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3");

        impfEnsureTable($pdo, 'impf_zeitraum', "CREATE TABLE impf_zeitraum (
            zeitraum_id INT NOT NULL AUTO_INCREMENT,
            bezeichnung VARCHAR(120) NOT NULL DEFAULT '',
            wochentag TINYINT NOT NULL,
            start TIME NOT NULL,
            ende TIME NOT NULL,
            impfortid INT NOT NULL,
            aktiv TINYINT NOT NULL DEFAULT 1,
            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (zeitraum_id),
            INDEX idx_impf_zeitraum_wochentag (wochentag),
            INDEX idx_impf_zeitraum_impfort (impfortid)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3");

        if (!impfTableHasColumn($pdo, 'impf_zeitraum', 'bezeichnung')) {
            $pdo->exec("ALTER TABLE impf_zeitraum ADD COLUMN bezeichnung VARCHAR(120) NOT NULL DEFAULT '' AFTER zeitraum_id");
        }

        impfEnsureTable($pdo, 'impf_zeitraum_impfstoff', "CREATE TABLE impf_zeitraum_impfstoff (
            zeitraum_id INT NOT NULL,
            impfstoff_id INT NOT NULL,
            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (zeitraum_id, impfstoff_id),
            INDEX idx_impf_zeitraum_impfstoff_impfstoff (impfstoff_id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3");

        impfEnsureTable($pdo, 'warteliste_zeitraum', "CREATE TABLE warteliste_zeitraum (
            warteid INT NOT NULL,
            zeitraum_id INT NOT NULL,
            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (warteid, zeitraum_id),
            INDEX idx_warteliste_zeitraum_zeitraum (zeitraum_id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3");

        if (impfTableExists($pdo, 'warteliste_zeitraum') && !impfTableHasIndex($pdo, 'warteliste_zeitraum', 'idx_warteliste_zeitraum_zeitraum')) {
            $pdo->exec("ALTER TABLE warteliste_zeitraum ADD INDEX idx_warteliste_zeitraum_zeitraum (zeitraum_id)");
        }

        if (impfTableExists($pdo, 'warteliste') && !impfTableHasColumn($pdo, 'warteliste', 'zeitraum_id')) {
            $pdo->exec("ALTER TABLE warteliste ADD COLUMN zeitraum_id INT NULL AFTER impfenzeitraum");
        }
        if (impfTableExists($pdo, 'warteliste') && !impfTableHasIndex($pdo, 'warteliste', 'idx_warteliste_zeitraum')) {
            $pdo->exec("ALTER TABLE warteliste ADD INDEX idx_warteliste_zeitraum (zeitraum_id)");
        }

        impfWorkflowMigrateLegacyPlans($pdo);
        impfWorkflowMigrateLegacyWartelisteZeitraeume($pdo);
    }
}

if (!function_exists('impfWorkflowGetMeta')) {
    function impfWorkflowGetMeta(PDO $pdo, string $key): ?string
    {
        $st = $pdo->prepare("SELECT meta_value
                             FROM impf_workflow_meta
                             WHERE meta_key = :meta_key
                             LIMIT 1");
        $st->execute(['meta_key' => $key]);
        $value = $st->fetchColumn();
        return ($value === false) ? null : (string)$value;
    }
}

if (!function_exists('impfWorkflowSetMeta')) {
    function impfWorkflowSetMeta(PDO $pdo, string $key, string $value): void
    {
        $st = $pdo->prepare("INSERT INTO impf_workflow_meta (meta_key, meta_value)
                             VALUES (:meta_key, :meta_value)
                             ON DUPLICATE KEY UPDATE meta_value = VALUES(meta_value)");
        $st->execute([
            'meta_key' => $key,
            'meta_value' => $value,
        ]);
    }
}

if (!function_exists('impfWorkflowMigrateLegacyPlans')) {
    function impfWorkflowMigrateLegacyPlans(PDO $pdo): void
    {
        if (impfWorkflowGetMeta($pdo, 'legacy_wochenplan_migrated') === '1') {
            return;
        }

        if (!impfTableExists($pdo, 'impfstoff_wochenplan')) {
            return;
        }

        $stLegacy = $pdo->query("SELECT plan_id, impfstoff_id, wochentag, start, ende, impfortid, aktiv
                                 FROM impfstoff_wochenplan");
        $legacyRows = $stLegacy ? $stLegacy->fetchAll(PDO::FETCH_ASSOC) : [];
        if (empty($legacyRows)) {
            impfWorkflowSetMeta($pdo, 'legacy_wochenplan_migrated', '1');
            return;
        }

        $manageTransaction = !$pdo->inTransaction();
        if ($manageTransaction) {
            $pdo->beginTransaction();
        }

        try {
            $stFindZeitraum = $pdo->prepare("SELECT zeitraum_id
                                             FROM impf_zeitraum
                                             WHERE wochentag = :wochentag
                                               AND start = :start
                                               AND ende = :ende
                                               AND impfortid = :impfortid
                                               AND aktiv = :aktiv
                                             LIMIT 1");
            $stInsertZeitraum = $pdo->prepare("INSERT INTO impf_zeitraum
                (bezeichnung, wochentag, start, ende, impfortid, aktiv)
                VALUES
                ('', :wochentag, :start, :ende, :impfortid, :aktiv)");
            $stInsertMap = $pdo->prepare("INSERT INTO impf_zeitraum_impfstoff
                (zeitraum_id, impfstoff_id)
                VALUES
                (:zeitraum_id, :impfstoff_id)
                ON DUPLICATE KEY UPDATE impfstoff_id = VALUES(impfstoff_id)");

            foreach ($legacyRows as $row) {
                $params = [
                    'wochentag' => (int)$row['wochentag'],
                    'start' => (string)$row['start'],
                    'ende' => (string)$row['ende'],
                    'impfortid' => (int)$row['impfortid'],
                    'aktiv' => (int)$row['aktiv'],
                ];

                $stFindZeitraum->execute($params);
                $zeitraumId = (int)($stFindZeitraum->fetchColumn() ?: 0);
                if ($zeitraumId <= 0) {
                    $stInsertZeitraum->execute($params);
                    $zeitraumId = (int)$pdo->lastInsertId();
                }

                if ($zeitraumId > 0 && (int)$row['impfstoff_id'] > 0) {
                    $stInsertMap->execute([
                        'zeitraum_id' => $zeitraumId,
                        'impfstoff_id' => (int)$row['impfstoff_id'],
                    ]);
                }
            }

            impfWorkflowSetMeta($pdo, 'legacy_wochenplan_migrated', '1');

            if ($manageTransaction) {
                $pdo->commit();
            }
        } catch (Throwable $e) {
            if ($manageTransaction && $pdo->inTransaction()) {
                $pdo->rollBack();
            }
            throw $e;
        }
    }
}

if (!function_exists('impfWorkflowMigrateLegacyWartelisteZeitraeume')) {
    function impfWorkflowMigrateLegacyWartelisteZeitraeume(PDO $pdo): void
    {
        if (!impfTableExists($pdo, 'warteliste') || !impfTableExists($pdo, 'warteliste_zeitraum')) {
            return;
        }

        if (impfWorkflowGetMeta($pdo, 'legacy_warteliste_zeitraeume_migrated') === '1') {
            return;
        }

        $manageTransaction = !$pdo->inTransaction();
        if ($manageTransaction) {
            $pdo->beginTransaction();
        }

        try {
            $pdo->exec("INSERT IGNORE INTO warteliste_zeitraum (warteid, zeitraum_id)
                        SELECT warteid, zeitraum_id
                          FROM warteliste
                         WHERE zeitraum_id IS NOT NULL
                           AND zeitraum_id > 0");
            impfWorkflowSetMeta($pdo, 'legacy_warteliste_zeitraeume_migrated', '1');

            if ($manageTransaction) {
                $pdo->commit();
            }
        } catch (Throwable $e) {
            if ($manageTransaction && $pdo->inTransaction()) {
                $pdo->rollBack();
            }
            throw $e;
        }
    }
}

if (!function_exists('impfCsvToIntList')) {
    function impfCsvToIntList(?string $csv): array
    {
        if ($csv === null || trim($csv) === '') {
            return [];
        }

        $werte = array_filter(array_map('trim', explode(',', $csv)), static function ($wert): bool {
            return $wert !== '';
        });

        return array_values(array_map('intval', $werte));
    }
}

if (!function_exists('impfZeitraumLabel')) {
    function impfZeitraumLabel(array $zeitraum, bool $includeName = true): string
    {
        $zeitText = impfWeekdayName((int)$zeitraum['wochentag']) . ' ' . substr((string)$zeitraum['start'], 0, 5) . '-' . substr((string)$zeitraum['ende'], 0, 5);
        $ort = trim((string)($zeitraum['anzeigename'] ?? '') . ' - ' . (string)($zeitraum['adresse'] ?? ''));
        $ortText = trim($ort, " -");
        if ($ortText !== '') {
            $zeitText .= ' (' . $ortText . ')';
        }

        $bezeichnung = trim((string)($zeitraum['bezeichnung'] ?? ''));
        if ($includeName && $bezeichnung !== '') {
            return $bezeichnung . ': ' . $zeitText;
        }

        return $zeitText;
    }
}

if (!function_exists('impfGetZeitraumRows')) {
    function impfGetZeitraumRows(PDO $pdo, bool $onlyActive = true): array
    {
        $sql = "SELECT z.zeitraum_id, z.bezeichnung, z.wochentag, z.start, z.ende, z.impfortid, z.aktiv, z.created_at,
                       o.anzeigename, o.adresse,
                       GROUP_CONCAT(DISTINCT m.impfstoff_id ORDER BY m.impfstoff_id SEPARATOR ',') AS impfstoff_ids,
                       GROUP_CONCAT(DISTINCT i.impfname ORDER BY i.impfname SEPARATOR '||') AS impfstoff_namen
                FROM impf_zeitraum z
                LEFT JOIN impfort o ON o.ortid = z.impfortid
                LEFT JOIN impf_zeitraum_impfstoff m ON m.zeitraum_id = z.zeitraum_id
                LEFT JOIN impfstoff i ON i.impfid = m.impfstoff_id";
        if ($onlyActive) {
            $sql .= " WHERE z.aktiv = 1";
        }
        $sql .= " GROUP BY z.zeitraum_id, z.bezeichnung, z.wochentag, z.start, z.ende, z.impfortid, z.aktiv, z.created_at, o.anzeigename, o.adresse
                  ORDER BY z.wochentag, z.start, z.ende, z.bezeichnung, z.zeitraum_id";

        $st = $pdo->prepare($sql);
        $st->execute();
        $rows = $st->fetchAll(PDO::FETCH_ASSOC);

        foreach ($rows as &$row) {
            $row['impfstoff_id_list'] = impfCsvToIntList($row['impfstoff_ids'] ?? null);
            $row['impfstoff_name_list'] = [];
            if (!empty($row['impfstoff_namen'])) {
                $row['impfstoff_name_list'] = array_values(array_filter(explode('||', (string)$row['impfstoff_namen']), static function ($name): bool {
                    return trim((string)$name) !== '';
                }));
            }
            $row['label'] = impfZeitraumLabel($row);
        }
        unset($row);

        return $rows;
    }
}

if (!function_exists('impfLoadZeitraumById')) {
    function impfLoadZeitraumById(PDO $pdo, int $zeitraumId, bool $onlyActive = true): ?array
    {
        if ($zeitraumId <= 0) {
            return null;
        }

        $sql = "SELECT z.zeitraum_id, z.bezeichnung, z.wochentag, z.start, z.ende, z.impfortid, z.aktiv, z.created_at,
                       o.anzeigename, o.adresse,
                       GROUP_CONCAT(DISTINCT m.impfstoff_id ORDER BY m.impfstoff_id SEPARATOR ',') AS impfstoff_ids,
                       GROUP_CONCAT(DISTINCT i.impfname ORDER BY i.impfname SEPARATOR '||') AS impfstoff_namen
                FROM impf_zeitraum z
                LEFT JOIN impfort o ON o.ortid = z.impfortid
                LEFT JOIN impf_zeitraum_impfstoff m ON m.zeitraum_id = z.zeitraum_id
                LEFT JOIN impfstoff i ON i.impfid = m.impfstoff_id
                WHERE z.zeitraum_id = :zeitraum_id";
        if ($onlyActive) {
            $sql .= " AND z.aktiv = 1";
        }
        $sql .= " GROUP BY z.zeitraum_id, z.bezeichnung, z.wochentag, z.start, z.ende, z.impfortid, z.aktiv, z.created_at, o.anzeigename, o.adresse
                  LIMIT 1";

        $st = $pdo->prepare($sql);
        $st->execute(['zeitraum_id' => $zeitraumId]);
        $row = $st->fetch(PDO::FETCH_ASSOC);
        if (!$row) {
            return null;
        }

        $row['impfstoff_id_list'] = impfCsvToIntList($row['impfstoff_ids'] ?? null);
        $row['impfstoff_name_list'] = [];
        if (!empty($row['impfstoff_namen'])) {
            $row['impfstoff_name_list'] = array_values(array_filter(explode('||', (string)$row['impfstoff_namen']), static function ($name): bool {
                return trim((string)$name) !== '';
            }));
        }
        $row['label'] = impfZeitraumLabel($row);

        return $row;
    }
}

if (!function_exists('impfGetZeitraeumeByImpfstoff')) {
    function impfGetZeitraeumeByImpfstoff(PDO $pdo, bool $onlyActive = true): array
    {
        $zeitraeume = impfGetZeitraumRows($pdo, $onlyActive);
        $result = [];

        foreach ($zeitraeume as $zeitraum) {
            foreach ($zeitraum['impfstoff_id_list'] as $impfstoffId) {
                if (!isset($result[$impfstoffId])) {
                    $result[$impfstoffId] = [];
                }
                $result[$impfstoffId][] = $zeitraum;
            }
        }

        return $result;
    }
}

if (!function_exists('impfGetWartelistenFormOptions')) {
    function impfGetWartelistenFormOptions(PDO $pdo): array
    {
        $impfstoffe = [];
        $zeitfenster = [];

        $stImpfstoffe = $pdo->prepare("SELECT r.impfstoff_id, i.impfname
                                       FROM impfstoff_workflow r
                                       INNER JOIN impfstoff i ON i.impfid = r.impfstoff_id
                                       WHERE (i.aktiv = 1 OR i.aktivwarteliste = 1 OR i.aktivtermin = 1 OR i.aktivgrippe = 1)
                                         AND r.dosen_pro_flasche > 0
                                       ORDER BY i.impfname");
        $stImpfstoffe->execute();
        foreach ($stImpfstoffe->fetchAll(PDO::FETCH_ASSOC) as $row) {
            $impfstoffe[(int)$row['impfstoff_id']] = (string)$row['impfname'];
        }

        $zeitraeumeByImpfstoff = impfGetZeitraeumeByImpfstoff($pdo, true);
        foreach ($zeitraeumeByImpfstoff as $impfstoffId => $zeitraeume) {
            if (!isset($impfstoffe[$impfstoffId])) {
                continue;
            }

            foreach ($zeitraeume as $zeitraum) {
                if (!isset($zeitfenster[$impfstoffId])) {
                    $zeitfenster[$impfstoffId] = [];
                }
                $zeitfenster[$impfstoffId][] = [
                    'id' => (int)$zeitraum['zeitraum_id'],
                    'label' => impfZeitraumLabel($zeitraum, false),
                ];
            }
        }

        foreach (array_keys($impfstoffe) as $impfstoffId) {
            if (empty($zeitfenster[$impfstoffId])) {
                unset($impfstoffe[$impfstoffId]);
            }
        }

        return [
            'impfstoffe' => $impfstoffe,
            'zeitfenster' => $zeitfenster,
        ];
    }
}

if (!function_exists('impfGetWartelistenZeitraeume')) {
    function impfGetWartelistenZeitraeume(PDO $pdo, int $warteid, bool $onlyActive = false): array
    {
        if ($warteid <= 0 || !impfTableExists($pdo, 'warteliste_zeitraum')) {
            return [];
        }

        $sql = "SELECT z.zeitraum_id, z.bezeichnung, z.wochentag, z.start, z.ende, z.impfortid, z.aktiv, z.created_at,
                       o.anzeigename, o.adresse
                FROM warteliste_zeitraum wz
                INNER JOIN impf_zeitraum z ON z.zeitraum_id = wz.zeitraum_id
                LEFT JOIN impfort o ON o.ortid = z.impfortid
                WHERE wz.warteid = :warteid";
        if ($onlyActive) {
            $sql .= " AND z.aktiv = 1";
        }
        $sql .= " ORDER BY z.wochentag, z.start, z.ende, z.bezeichnung, z.zeitraum_id";

        $st = $pdo->prepare($sql);
        $st->execute(['warteid' => $warteid]);
        $rows = $st->fetchAll(PDO::FETCH_ASSOC);

        foreach ($rows as &$row) {
            $row['label'] = impfZeitraumLabel($row);
        }
        unset($row);

        if (!empty($rows)) {
            return $rows;
        }

        $stFallback = $pdo->prepare("SELECT w.zeitraum_id, z.bezeichnung, z.wochentag, z.start, z.ende, z.impfortid, z.aktiv, z.created_at,
                                            o.anzeigename, o.adresse
                                     FROM warteliste w
                                     LEFT JOIN impf_zeitraum z ON z.zeitraum_id = w.zeitraum_id
                                     LEFT JOIN impfort o ON o.ortid = z.impfortid
                                     WHERE w.warteid = :warteid
                                       AND w.zeitraum_id IS NOT NULL
                                     LIMIT 1");
        $stFallback->execute(['warteid' => $warteid]);
        $row = $stFallback->fetch(PDO::FETCH_ASSOC);
        if (!$row) {
            return [];
        }

        $row['label'] = impfZeitraumLabel($row);
        return [$row];
    }
}

if (!function_exists('impfGetWartelistenZeitraeumeLabels')) {
    function impfGetWartelistenZeitraeumeLabels(PDO $pdo, int $warteid, bool $onlyActive = false): array
    {
        $rows = impfGetWartelistenZeitraeume($pdo, $warteid, $onlyActive);
        return array_values(array_map(static function (array $row): string {
            return (string)($row['label'] ?? '');
        }, $rows));
    }
}

if (!function_exists('impfGetWartelistenZeitraeumeLabelsMap')) {
    function impfGetWartelistenZeitraeumeLabelsMap(PDO $pdo, array $warteids, bool $onlyActive = false): array
    {
        $warteids = array_values(array_unique(array_filter(array_map('intval', $warteids), static function (int $warteid): bool {
            return $warteid > 0;
        })));

        if (empty($warteids) || !impfTableExists($pdo, 'warteliste_zeitraum')) {
            return [];
        }

        $result = [];
        foreach ($warteids as $warteid) {
            $result[$warteid] = [];
        }

        $placeholders = [];
        $params = [];
        foreach ($warteids as $index => $warteid) {
            $key = 'wid' . $index;
            $placeholders[] = ':' . $key;
            $params[$key] = $warteid;
        }
        $inList = implode(', ', $placeholders);

        $sql = "SELECT wz.warteid, z.zeitraum_id, z.bezeichnung, z.wochentag, z.start, z.ende, z.impfortid, z.aktiv, z.created_at,
                       o.anzeigename, o.adresse
                FROM warteliste_zeitraum wz
                INNER JOIN impf_zeitraum z ON z.zeitraum_id = wz.zeitraum_id
                LEFT JOIN impfort o ON o.ortid = z.impfortid
                WHERE wz.warteid IN (" . $inList . ")";
        if ($onlyActive) {
            $sql .= " AND z.aktiv = 1";
        }
        $sql .= " ORDER BY wz.warteid, z.wochentag, z.start, z.ende, z.bezeichnung, z.zeitraum_id";

        $st = $pdo->prepare($sql);
        $st->execute($params);
        $rows = $st->fetchAll(PDO::FETCH_ASSOC);

        foreach ($rows as $row) {
            $warteid = (int)($row['warteid'] ?? 0);
            if ($warteid <= 0) {
                continue;
            }
            $row['label'] = impfZeitraumLabel($row);
            $result[$warteid][] = (string)$row['label'];
        }

        $missing = array_values(array_filter($warteids, static function (int $warteid) use ($result): bool {
            return empty($result[$warteid]);
        }));

        if (empty($missing)) {
            return $result;
        }

        $fallbackPlaceholders = [];
        $fallbackParams = [];
        foreach ($missing as $index => $warteid) {
            $key = 'f_wid' . $index;
            $fallbackPlaceholders[] = ':' . $key;
            $fallbackParams[$key] = $warteid;
        }
        $fallbackInList = implode(', ', $fallbackPlaceholders);

        $fallbackSql = "SELECT w.warteid, w.zeitraum_id, z.bezeichnung, z.wochentag, z.start, z.ende, z.impfortid, z.aktiv, z.created_at,
                               o.anzeigename, o.adresse
                        FROM warteliste w
                        LEFT JOIN impf_zeitraum z ON z.zeitraum_id = w.zeitraum_id
                        LEFT JOIN impfort o ON o.ortid = z.impfortid
                        WHERE w.warteid IN (" . $fallbackInList . ")
                          AND w.zeitraum_id IS NOT NULL";
        if ($onlyActive) {
            $fallbackSql .= " AND z.aktiv = 1";
        }

        $stFallback = $pdo->prepare($fallbackSql);
        $stFallback->execute($fallbackParams);
        $fallbackRows = $stFallback->fetchAll(PDO::FETCH_ASSOC);

        foreach ($fallbackRows as $row) {
            $warteid = (int)($row['warteid'] ?? 0);
            if ($warteid <= 0) {
                continue;
            }
            $row['label'] = impfZeitraumLabel($row);
            $result[$warteid] = [(string)$row['label']];
        }

        return $result;
    }
}

if (!function_exists('impfSetWartelistenZeitraeume')) {
    function impfSetWartelistenZeitraeume(PDO $pdo, int $warteid, $zeitraumIds): void
    {
        $zeitraumIds = impfNormalizeZeitraumIds($zeitraumIds);
        if ($warteid <= 0) {
            throw new InvalidArgumentException('Unguelige Wartelisten-ID.');
        }

        $manageTransaction = !$pdo->inTransaction();
        if ($manageTransaction) {
            $pdo->beginTransaction();
        }

        try {
            $stDelete = $pdo->prepare("DELETE FROM warteliste_zeitraum WHERE warteid = :warteid");
            $stDelete->execute(['warteid' => $warteid]);

            if (!empty($zeitraumIds)) {
                $stInsert = $pdo->prepare("INSERT INTO warteliste_zeitraum (warteid, zeitraum_id)
                                           VALUES (:warteid, :zeitraum_id)");
                foreach ($zeitraumIds as $zeitraumId) {
                    $stInsert->execute([
                        'warteid' => $warteid,
                        'zeitraum_id' => $zeitraumId,
                    ]);
                }
            }

            if ($manageTransaction) {
                $pdo->commit();
            }
        } catch (Throwable $e) {
            if ($manageTransaction && $pdo->inTransaction()) {
                $pdo->rollBack();
            }
            throw $e;
        }
    }
}

if (!function_exists('impfCreateWaitlistEntryForPerson')) {
    function impfCreateWaitlistEntryForPerson(
        PDO $pdo,
        int $personId,
        int $impfstoffId,
        $zeitraumIds,
        int $impfart,
        ?string $letzteImpfung = null,
        int $checked = 1
    ): array {
        if ($personId <= 0) {
            return [false, 'Keine gueltige Person uebergeben.', null];
        }
        if ($impfstoffId <= 0) {
            return [false, 'Bitte einen Impfstoff auswaehlen.', null];
        }
        if ($impfart < 1 || $impfart > 4) {
            return [false, 'Bitte eine gueltige Impfungsart auswaehlen.', null];
        }

        $zeitraumIds = impfNormalizeZeitraumIds($zeitraumIds);
        if (empty($zeitraumIds)) {
            return [false, 'Bitte mindestens ein Zeitfenster auswaehlen.', null];
        }

        $letzteImpfung = $letzteImpfung !== null ? trim($letzteImpfung) : null;
        if ($impfart === 1) {
            $letzteImpfung = null;
        } elseif ($letzteImpfung === null || $letzteImpfung === '') {
            return [false, 'Bitte das Datum der letzten Impfung angeben.', null];
        } elseif (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $letzteImpfung)) {
            return [false, 'Das Datum der letzten Impfung ist ungueltig.', null];
        }

        $stPerson = $pdo->prepare("SELECT person_id, patientenart, vorname, nachname
                                   FROM persons
                                   WHERE person_id = :pid
                                   LIMIT 1");
        $stPerson->execute(['pid' => $personId]);
        $person = $stPerson->fetch(PDO::FETCH_ASSOC);
        if (!$person) {
            return [false, 'Die Person wurde nicht gefunden.', null];
        }

        $zeitraumRows = [];
        $zeitraumLabels = [];
        foreach ($zeitraumIds as $zeitraumId) {
            $row = impfLoadZeitraumById($pdo, $zeitraumId, true);
            if (!$row) {
                return [false, 'Mindestens ein ausgewaehltes Zeitfenster ist nicht mehr verfuegbar.', null];
            }
            if (!in_array($impfstoffId, $row['impfstoff_id_list'] ?? [], true)) {
                return [false, 'Impfstoff und Zeitfenster passen nicht zusammen.', null];
            }
            $zeitraumRows[$zeitraumId] = $row;
            $zeitraumLabels[] = (string)$row['label'];
        }

        $stDup = $pdo->prepare("SELECT w.warteid
                                FROM warteliste w
                                LEFT JOIN warteliste_zeitraum wz ON wz.warteid = w.warteid
                                WHERE w.userid = :uid
                                  AND w.checked IN (0, 1)
                                  AND w.impfstoff = :impfstoff
                                  AND w.impfart = :impfart
                                GROUP BY w.warteid
                                LIMIT 1");
        $stDup->execute([
            'uid' => $personId,
            'impfstoff' => $impfstoffId,
            'impfart' => $impfart,
        ]);
        if ($stDup->fetchColumn()) {
            return [false, 'Diese Anfrage ist fuer diese Person bereits auf der aktiven Warteliste vorhanden.', null];
        }

        $patientenart = ((int)($person['patientenart'] ?? 0) === 1) ? 1 : 0;
        $hash = bin2hex(random_bytes(16));
        $checkedValue = ($checked === 0) ? 0 : 1;
        $primaerZeitraumId = (int)$zeitraumIds[0];
        $impfenzeitraum = implode(' | ', $zeitraumLabels);

        $manageTransaction = !$pdo->inTransaction();
        if ($manageTransaction) {
            $pdo->beginTransaction();
        }

        try {
            $stInsert = $pdo->prepare("INSERT INTO warteliste
                (userid, checked, hash, impfenangebot, impfstoff, Patientenart, Impfaufklaerung, WeitereFragen, impfart, impfenmit, letzteimpfung, impfenzeitraum, zeitraum_id, date_created)
                VALUES
                (:userid, :checked, :hash, 1, :impfstoff, :patientenart, 0, 0, :impfart, '', :letzteimpfung, :impfenzeitraum, :zeitraum_id, NOW())");
            $stInsert->execute([
                'userid' => $personId,
                'checked' => $checkedValue,
                'hash' => $hash,
                'impfstoff' => $impfstoffId,
                'patientenart' => $patientenart,
                'impfart' => $impfart,
                'letzteimpfung' => $letzteImpfung,
                'impfenzeitraum' => $impfenzeitraum,
                'zeitraum_id' => $primaerZeitraumId,
            ]);

            $warteid = (int)$pdo->lastInsertId();
            impfSetWartelistenZeitraeume($pdo, $warteid, $zeitraumIds);

            if ($manageTransaction) {
                $pdo->commit();
            }

            $personName = trim((string)$person['vorname'] . ' ' . (string)$person['nachname']);
            return [true, 'Wartelistenplatz fuer ' . $personName . ' wurde gespeichert.', $warteid];
        } catch (Throwable $e) {
            if ($manageTransaction && $pdo->inTransaction()) {
                $pdo->rollBack();
            }
            throw $e;
        }
    }
}
