<?php

require __DIR__ . '/bootstrap.php';

use KeyLair\PageStyle;
use KeyLair\Auth;
use KeyLair\Database;
use KeyLair\Settings;

header('Content-Type: text/css; charset=utf-8');

$key = $_GET['key'] ?? '';

if (!PageStyle::isValidKey($key)) {
    http_response_code(404);
    header('Cache-Control: no-store');
    echo '/* unknown stylesheet */';
    exit;
}

// Preview mode shows the pending draft (if any) instead of the published
// version — but only to a logged-in admin. Anyone else requesting the same
// URL with ?preview=1 still gets the normal published CSS, so a draft can
// never leak to real visitors before it's published. Deliberately ignores
// the active theme: an admin previewing their own work-in-progress custom
// CSS should see it rendered even while a built-in theme is what's
// actually live for everyone else.
$wantsPreview = ($_GET['preview'] ?? '') === '1';

if ($wantsPreview && Auth::check()) {
    header('Cache-Control: no-store');
    echo PageStyle::previewContent($key);
    exit;
}

// The backend (admin panel) and the frontend (customer-facing claim/
// product pages) have independent themes: the frontend theme is one
// site-wide setting (admin/design.php, shown to every visitor), but the
// backend theme is a personal preference each admin sets for themselves
// (account.php) so one admin's choice never affects what a colleague
// sees. layout_header.php (and every pre-login admin page) requests its
// stylesheets with ?admin=1 to signal "resolve the backend theme here" —
// falls back to 'default' if the logged-in admin never set one, not to
// the frontend theme, since the two are deliberately decoupled.
$isAdminScoped = ($_GET['admin'] ?? '') === '1';

// Lets a logged-in admin preview any frontend theme (admin/design.php's
// "Preview" links) without actually changing the site-wide setting for
// real visitors. Frontend-only on purpose -- there's no equivalent need
// for the backend, since that's a personal choice already applied the
// moment it's picked.
$previewTheme = $_GET['preview_theme'] ?? null;

// A product can override the site-wide frontend theme for just its own
// claim page (admin/product_form.php's "Page theme"). Resolved from the
// DB by ID rather than trusting a theme name directly in the query string,
// so a visitor can never request an arbitrary/invalid theme this way --
// only whatever a real product actually has configured. Real visitors
// need this (not just logged-in admins), unlike preview_theme, so it's
// safe to cache: the product ID is part of the URL/cache key itself,
// unlike the backend's ?admin=1 path where the difference lives only in
// a cookie the cache can't see.
$productId = isset($_GET['product_id']) ? (int) $_GET['product_id'] : 0;
$productTheme = null;
$productBackgroundImage = null;
if (!$isAdminScoped && $productId > 0) {
    $stmt = Database::connection()->prepare('SELECT theme, background_image_path FROM products WHERE id = :id');
    $stmt->execute(['id' => $productId]);
    $productRow = $stmt->fetch();
    if ($productRow && $productRow['theme'] !== null && in_array($productRow['theme'], Settings::THEMES, true)) {
        $productTheme = $productRow['theme'];
        $productBackgroundImage = $productRow['background_image_path'];
    }
}

if (!$isAdminScoped && $previewTheme !== null && Auth::check() && in_array($previewTheme, Settings::THEMES, true)) {
    $theme = $previewTheme;
} elseif (!$isAdminScoped && $productTheme !== null) {
    $theme = $productTheme;
} elseif ($isAdminScoped && Auth::check()) {
    $personalTheme = Auth::user()['theme'] ?? null;
    $theme = ($personalTheme !== null && in_array($personalTheme, Settings::THEMES, true)) ? $personalTheme : 'default';
} elseif ($isAdminScoped) {
    // Admin-scoped request but not actually logged in (e.g. login.php before auth) -- no personal preference to apply.
    $theme = 'default';
} else {
    $theme = Settings::theme();
}

// Admin-scoped output is personal (varies per logged-in admin, same URL
// for everyone), so it must never be reused across requests: a proxy or
// CDN honoring a cacheable response here could serve one admin's chosen
// theme to a completely different admin, and even the browser's own
// cache could do the same across two admins sharing one browser profile
// in quick succession (this file has no ETag/Last-Modified for a
// revalidating "private" cache to safely fall back on). A theme preview
// is just as personal (only the requesting admin should see it) and must
// never be cached either. The genuinely-identical, real frontend path
// keeps a short shared cache.
header(($isAdminScoped || $previewTheme !== null) ? 'Cache-Control: no-store' : 'Cache-Control: public, max-age=300');

// 'tokens' is never independently customizable (not in
// PageStyle::CUSTOMIZABLE_PAGES) — it only ever varies by *built-in*
// theme, even under Custom, where it's just the shipped default. A
// designer wanting different colors under Custom puts a :root{...} block
// directly in their claim-overview.css/product-page.css upload instead —
// it loads after tokens.css and wins the cascade for whichever variables
// it redefines. Every other admin-uploaded custom CSS only applies under
// the Custom theme — a built-in theme (default/light/steam) always means
// the shipped template, so switching themes can never accidentally mix
// an uploaded override with the wrong palette.
if ($key === 'tokens') {
    echo ($theme === 'custom') ? PageStyle::defaultContent('tokens') : PageStyle::builtInThemeTokens($theme);
} elseif ($theme === 'custom') {
    echo PageStyle::activeContent($key);
} else {
    echo PageStyle::defaultContent($key);
}

// The uploaded background image is a frontend-only concept (never shown
// in the backend, even under a personal Custom theme there) — appended to
// the end of every frontend stylesheet's own output (not just
// tokens.css), since each page's stylesheet sets its own
// body{background:...} and loads *after* tokens.css in <head>, so an
// equal-specificity rule only in tokens.css would just lose the cascade
// to it. Appending it here instead means it is always the last body{}
// rule in whichever file happens to load last. A product with its own
// Custom theme override uses its own background image (or none at all)
// instead of falling back to the site-wide one -- fully independent, not
// a fallback chain.
if (!$isAdminScoped && $theme === 'custom') {
    $backgroundImage = ($productTheme === 'custom') ? $productBackgroundImage : Settings::backgroundImagePath();
    if ($backgroundImage) {
        $safeUrl = str_replace(["'", '\\', "\n", "\r"], '', $backgroundImage);
        echo "\nbody{background-image:url('{$safeUrl}') !important; background-size:cover; background-position:center center; background-repeat:no-repeat; background-attachment:fixed;}\n";
    }
}
