<?php

declare(strict_types=1);

/**
 * Release guard for Tailwind classes that are ASSEMBLED AT RUNTIME.
 *
 * `class="bg-<?= $s['color'] ?>-50"` is invisible to Tailwind's content scanner: it reads
 * the raw file text and finds "bg-" and "-50", never "bg-fuchsia-50". The class is simply
 * absent from the compiled stylesheet and the element renders unstyled — no error, no log,
 * nothing to notice. That is exactly how the dashboard's Albums tile (fuchsia) and Pages
 * tile (cyan) lost their colour when the Play CDN was replaced; a browser diff caught them,
 * and this guard exists so the next one is caught before it ships.
 *
 * The requirement is DERIVED from the templates, never restated: this script finds the
 * interpolation sites itself, collects the colour tokens that appear in the same file, and
 * demands the cross-product be present in the built CSS. It is therefore independent of
 * the `safelist` in build/admin-css/tailwind.config.js — if the two disagree, this fails.
 * A guard that shared the safelist's list would go stale at the same moment it does.
 *
 * Run from the repo root:  php bin/check-dynamic-classes.php
 */

$root = dirname(__DIR__);
$css  = $root . '/templates/admin/assets/tailwind.css';
if (!is_file($css)) {
    fwrite(STDERR, "DYNAMIC_CLASSES_FAIL: missing " . $css . "\n");
    exit(1);
}
$sheet = (string) file_get_contents($css);

 
const TW_COLORS = [
    'slate', 'gray', 'zinc', 'neutral', 'stone', 'red', 'orange', 'amber', 'yellow',
    'lime', 'green', 'emerald', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'violet',
    'purple', 'fuchsia', 'pink', 'rose', 'primary',
];

$dirs = ['templates', 'src', 'themes', 'plugins'];
$files = [];
foreach ($dirs as $d) {
    if (!is_dir($root . '/' . $d)) {
        continue;
    }
    $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($root . '/' . $d, FilesystemIterator::SKIP_DOTS));
    foreach ($it as $f) {
         
         
        if ($f->isFile() && (str_ends_with($f->getFilename(), '.php') || str_ends_with($f->getFilename(), '.js'))) {
            $files[] = $f->getPathname();
        }
    }
}

 
 
$PATTERNS = [
     
    '/((?:hover:|focus:|active:|group-hover:|sm:|md:|lg:|xl:)?(?:bg|text|border|ring|from|to|via|fill|stroke|divide|decoration|placeholder|accent|outline|caret))-<\?=[^?]{1,80}\?>(-[0-9]{2,3}(?:\/[0-9]{1,3})?)/',
     
    '/[\'"]((?:hover:|focus:)?(?:bg|text|border|ring|fill|stroke))-[\'"]\s*\.\s*\$[A-Za-z_][A-Za-z0-9_\[\]\'"\-]*\s*\.\s*[\'"](-[0-9]{2,3}(?:\/[0-9]{1,3})?)/',
     
     
    '/[\'"]((?:hover:|focus:)?(?:bg|text|border|ring|fill|stroke))-[\'"]\s*\+\s*[A-Za-z_$][A-Za-z0-9_.\[\]\'"]*\s*\+\s*[\'"](-[0-9]{2,3}(?:\/[0-9]{1,3})?)/',
];

$required = [];   
$sites    = 0;

foreach ($files as $path) {
    $src = (string) file_get_contents($path);
    $hits = [];
    foreach ($PATTERNS as $re) {
        if (preg_match_all($re, $src, $m, PREG_SET_ORDER)) {
            foreach ($m as $hit) {
                $hits[] = [$hit[1], $hit[2]];
            }
        }
    }
    if (!$hits) {
        continue;
    }
    $sites += count($hits);

     
     
    $colors = [];
    foreach (TW_COLORS as $c) {
        if (preg_match("/'" . $c . "'/", $src) || preg_match('/"' . $c . '"/', $src)) {
            $colors[] = $c;
        }
    }
    if (!$colors) {
        continue;
    }

    foreach ($hits as [$prefix, $suffix]) {
        foreach ($colors as $c) {
            $required[$prefix . '-' . $c . $suffix][] = basename($path);
        }
    }
}

 
$escape = static function (string $cls): string {
    return preg_replace('/([:\/.])/', '\\\\$1', $cls) ?? $cls;
};

$missing = [];
foreach ($required as $cls => $where) {
    $needle = '.' . $escape($cls);
     
    if (!str_contains($sheet, $needle . '{') && !str_contains($sheet, $needle . ':') && !str_contains($sheet, $needle . ',')) {
        $missing[$cls] = array_unique($where);
    }
}

if ($missing) {
    fwrite(STDERR, "DYNAMIC_CLASSES_FAIL: " . count($missing) . " runtime-built class(es) absent from the compiled admin stylesheet.\n");
    fwrite(STDERR, "  Add them to `safelist` in build/admin-css/tailwind.config.js, then run bin/build-admin-css.sh\n");
    $shown = 0;
    foreach ($missing as $cls => $where) {
        fwrite(STDERR, '    ' . $cls . '   (' . implode(', ', $where) . ")\n");
        if (++$shown >= 40) {
            fwrite(STDERR, "    ... and " . (count($missing) - $shown) . " more\n");
            break;
        }
    }
    exit(1);
}

fwrite(STDOUT, "DYNAMIC_CLASSES_OK (" . $sites . " interpolation sites, " . count($required) . " classes verified present)\n");
exit(0);
