<?php
declare(strict_types=1);

/**
 * Zentrale CW-Pipeline fuer Transformationen des fertig gerenderten Frontend-Dokuments.
 *
 * Module duerfen hier Filter registrieren, statt eigene ob_start()-Callbacks zu verwenden.
 * CW besitzt den einzigen Dokumentpuffer und fuehrt die Filter kontrolliert aus.
 */
function cw_register_frontend_document_filter(string $key, callable $filter, int $priority = 100): void
{
    $key = preg_replace('/[^a-z0-9_.-]/', '', strtolower(trim($key))) ?: '';
    if ($key === '') {
        throw new InvalidArgumentException('Ungueltige Kennung fuer den Frontend-Dokumentfilter.');
    }

    $GLOBALS['cw_frontend_document_filters'][$key] = [
        'key' => $key,
        'priority' => max(-10000, min(10000, $priority)),
        'filter' => $filter,
    ];
}

/** @return list<array{key:string,priority:int,filter:callable}> */
function cw_frontend_document_filters(): array
{
    $filters = [];
    foreach ((array)($GLOBALS['cw_frontend_document_filters'] ?? []) as $definition) {
        if (!is_array($definition) || !is_callable($definition['filter'] ?? null)) continue;
        $filters[] = $definition;
    }

    usort(
        $filters,
        static fn(array $a, array $b): int => ((int)$a['priority'] <=> (int)$b['priority'])
            ?: strcmp((string)$a['key'], (string)$b['key'])
    );

    return $filters;
}

function cw_frontend_document_filters_active(): bool
{
    return cw_frontend_document_filters() !== [];
}

function cw_frontend_document_apply_filters(string $html, array $route = []): string
{
    foreach (cw_frontend_document_filters() as $definition) {
        $key = (string)$definition['key'];
        try {
            $result = ($definition['filter'])($html, $route);
            if (!is_string($result)) {
                throw new RuntimeException('Frontend-Dokumentfilter muss einen String zurueckgeben.');
            }
            $html = $result;
        } catch (Throwable $error) {
            error_log('CW Frontend-Dokumentfilter ' . $key . ': ' . $error->getMessage());

            // Lokal/Development soll der zentrale CW Error Handler den Fehler sichtbar machen.
            // Auf Produktion bleibt die Website fail-open und der vorherige Dokumentstand wird ausgeliefert.
            if (function_exists('cw_error_show_details') && cw_error_show_details()) {
                throw $error;
            }
        }
    }

    return $html;
}
