<?php
declare(strict_types=1);

if (!function_exists('str_contains')) {
    function str_contains(string $haystack, string $needle): bool
    {
        return $needle === '' || strpos($haystack, $needle) !== false;
    }
}
if (!function_exists('str_starts_with')) {
    function str_starts_with(string $haystack, string $needle): bool
    {
        return $needle === '' || strncmp($haystack, $needle, strlen($needle)) === 0;
    }
}

function theme_project_root(): string
{
    return dirname(__DIR__, 2);
}

function theme_tokens_json_path(): string
{
    return theme_project_root() . '/data/clientarea_default_theme_tokens.json';
}

function theme_pack_json_path(): string
{
    return THEME_ROOT . '/theme_pack.json';
}

function theme_override_css_path(): string
{
    return THEME_ROOT . '/assets_custom/css/theme-tokens-override.css';
}

function theme_site_brand_json_path(): string
{
    return theme_project_root() . '/data/clientarea_default_theme_site.json';
}

function theme_site_brand_css_path(): string
{
    return THEME_ROOT . '/assets_custom/css/theme-site-brand.css';
}

/** @return array<string, string> */
function theme_site_brand_defaults(): array
{
    return [
        'site_name' => '云脉智慧平台',
        'logo_url' => '',
        'footer_text' => '云脉智慧平台',
        'support_email' => '',
        'welcome_title' => '欢迎使用云脉智慧平台',
        'welcome_subtitle' => '管理您的产品与服务',
    ];
}

/** @return array<string, string> */
function theme_site_brand_read(): array
{
    $def = theme_site_brand_defaults();
    $p = theme_site_brand_json_path();
    if (!is_file($p)) {
        return $def;
    }
    $j = json_decode((string) file_get_contents($p), true);
    if (!is_array($j)) {
        return $def;
    }
    $out = $def;
    foreach ($def as $k => $v) {
        if (isset($j[$k]) && is_string($j[$k])) {
            $out[$k] = mb_substr($j[$k], 0, 500);
        }
    }
    if ($out['logo_url'] !== '' && !preg_match('#^https?://#i', $out['logo_url'])) {
        $out['logo_url'] = '';
    }
    if ($out['support_email'] !== '' && !filter_var($out['support_email'], FILTER_VALIDATE_EMAIL)) {
        $out['support_email'] = '';
    }
    return $out;
}

/** @param array<string, string> $brand */
function theme_site_brand_write(array $brand): bool
{
    $def = theme_site_brand_defaults();
    $clean = [];
    foreach ($def as $k => $v) {
        $clean[$k] = isset($brand[$k]) && is_string($brand[$k]) ? mb_substr(trim($brand[$k]), 0, 500) : $v;
    }
    if ($clean['logo_url'] !== '' && !preg_match('#^https?://#i', $clean['logo_url'])) {
        return false;
    }
    if ($clean['support_email'] !== '' && !filter_var($clean['support_email'], FILTER_VALIDATE_EMAIL)) {
        return false;
    }
    $dir = dirname(theme_site_brand_json_path());
    if (!is_dir($dir)) {
        @mkdir($dir, 0750, true);
    }
    if (file_put_contents(
        theme_site_brand_json_path(),
        json_encode($clean, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n"
    ) === false) {
        return false;
    }
    return theme_site_brand_regenerate_css($clean);
}

/** @param array<string, string> $brand */
function theme_site_brand_regenerate_css(array $brand): bool
{
    $name = str_replace(['\\', '"'], '', $brand['site_name']);
    $css = "/* 站点品牌：由主题后台「站点品牌」保存生成 */\n";
    $css .= ":root {\n  --theme-site-name: \"{$name}\";\n}\n";
    $css .= ".theme-brand-welcome-title::before { content: var(--theme-site-name); }\n";
    if ($brand['logo_url'] !== '') {
        $url = str_replace(['\\', '"', ')'], '', $brand['logo_url']);
        $css .= ".theme-brand-logo { background-image: url(\"{$url}\"); background-size: contain; background-repeat: no-repeat; background-position: left center; min-height: 36px; }\n";
    }
    $path = theme_site_brand_css_path();
    $d = dirname($path);
    if (!is_dir($d)) {
        @mkdir($d, 0755, true);
    }
    return file_put_contents($path, $css) !== false;
}

/** @return array<string, string> */
function theme_token_defaults(): array
{
    return [
        '--nq-primary' => '#1d5cff',
        '--nq-primary-hover' => '#0a46dc',
        '--nq-text' => '#262626',
        '--nq-muted' => '#8c8c8c',
        '--nq-border' => '#f0f0f0',
        '--nq-input-bg' => '#f5f5f5',
        '--blue' => '#1d5cff',
        '--bg-body' => '#fafafa',
        '--bg-card' => '#fff',
        '--text-primary' => '#262626',
        '--text-secondary' => '#595959',
        '--text-muted' => '#8c8c8c',
        '--border-color' => '#f0f0f0',
    ];
}

/** @return array<string, string> */
function theme_token_allowed_keys(): array
{
    return array_keys(theme_token_defaults());
}

function theme_valid_hex_color(string $s): bool
{
    return (bool) preg_match('/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/', $s);
}

/** @return array<string, string> */
function theme_tokens_read(): array
{
    $path = theme_tokens_json_path();
    $defaults = theme_token_defaults();
    if (!is_file($path)) {
        return $defaults;
    }
    $raw = file_get_contents($path);
    if ($raw === false) {
        return $defaults;
    }
    $j = json_decode($raw, true);
    if (!is_array($j)) {
        return $defaults;
    }
    $out = $defaults;
    foreach (theme_token_allowed_keys() as $k) {
        if (isset($j[$k]) && is_string($j[$k]) && theme_valid_hex_color($j[$k])) {
            $out[$k] = $j[$k];
        }
    }
    return $out;
}

/** @param array<string, string> $tokens */
function theme_tokens_write(array $tokens): bool
{
    $allowed = theme_token_allowed_keys();
    $clean = [];
    foreach ($allowed as $k) {
        if (!isset($tokens[$k]) || !is_string($tokens[$k]) || !theme_valid_hex_color($tokens[$k])) {
            return false;
        }
        $clean[$k] = $tokens[$k];
    }
    $dir = dirname(theme_tokens_json_path());
    if (!is_dir($dir)) {
        @mkdir($dir, 0750, true);
    }
    if (file_put_contents(
        theme_tokens_json_path(),
        json_encode($clean, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n"
    ) === false) {
        return false;
    }
    return theme_tokens_regenerate_css($clean);
}

/** @param array<string, string> $tokens */
function theme_tokens_regenerate_css(array $tokens): bool
{
    $lines = [':root {', '/* 本文件由「default 主题工作台」生成，勿手改；在后台「主题色」保存即可 */'];
    foreach ($tokens as $k => $v) {
        $lines[] = '  ' . $k . ': ' . $v . ';';
    }
    $lines[] = '}';
    $lines[] = '';
    $css = implode("\n", $lines);
    $path = theme_override_css_path();
    $d = dirname($path);
    if (!is_dir($d)) {
        @mkdir($d, 0755, true);
    }
    return file_put_contents($path, $css) !== false;
}

/** @return array{id: string, version: string, updated_at: string} */
function theme_pack_read(): array
{
    $def = ['id' => 'clientarea-default', 'version' => '1.0.0', 'updated_at' => date('c')];
    $p = theme_pack_json_path();
    if (!is_file($p)) {
        return $def;
    }
    $j = json_decode((string) file_get_contents($p), true);
    if (!is_array($j)) {
        return $def;
    }
    $id = isset($j['id']) && is_string($j['id']) ? preg_replace('/[^a-z0-9.-]/i', '', $j['id']) : $def['id'];
    $ver = isset($j['version']) && is_string($j['version']) ? preg_replace('/[^0-9a-zA-Z._-]/', '', $j['version']) : $def['version'];
    if ($id === '') {
        $id = $def['id'];
    }
    if ($ver === '') {
        $ver = $def['version'];
    }
    $updated = isset($j['updated_at']) && is_string($j['updated_at']) ? $j['updated_at'] : $def['updated_at'];
    return ['id' => $id, 'version' => $ver, 'updated_at' => $updated];
}

function theme_pack_write_version(string $version): bool
{
    $version = preg_replace('/[^0-9a-zA-Z._-]/', '', $version) ?? '';
    if ($version === '') {
        return false;
    }
    $cur = theme_pack_read();
    $cur['version'] = $version;
    $cur['updated_at'] = date('c');
    return file_put_contents(
        theme_pack_json_path(),
        json_encode($cur, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n"
    ) !== false;
}

function theme_editor_http_get(string $url, int $maxBytes = 262_144): ?string
{
    if (!preg_match('#^https?://#i', $url)) {
        return null;
    }
    $ctx = stream_context_create([
        'http' => [
            'timeout' => 20,
            'header' => "User-Agent: YuserThemeEditor/1\r\nAccept: application/json,*/*\r\n",
        ],
        'ssl' => [
            'verify_peer' => true,
            'verify_peer_name' => true,
        ],
    ]);
    $data = @file_get_contents($url, false, $ctx, 0, $maxBytes + 1);
    if ($data === false || strlen($data) > $maxBytes) {
        return null;
    }
    return $data;
}

/** @return array<string, mixed>|null */
function theme_editor_fetch_manifest(string $url, ?string $appendQuery): ?array
{
    $u = $url;
    if ($appendQuery !== null && $appendQuery !== '') {
        $u .= (str_contains($url, '?') ? '&' : '?') . $appendQuery;
    }
    $raw = theme_editor_http_get($u, 262_144);
    if ($raw === null) {
        return null;
    }
    $j = json_decode($raw, true);
    return is_array($j) ? $j : null;
}

/** @return string[] */
function theme_zip_allowed_extensions(): array
{
    return [
        'tpl', 'css', 'js', 'html', 'htm', 'json', 'svg', 'txt', 'md', 'map',
        'woff', 'woff2', 'ttf', 'otf', 'eot',
    ];
}

function theme_zip_entry_ext_ok(string $name): bool
{
    $bn = basename(str_replace('\\', '/', $name));
    if ($bn === '' || str_starts_with($bn, '.')) {
        return false;
    }
    $ext = strtolower(pathinfo($bn, PATHINFO_EXTENSION));
    return in_array($ext, theme_zip_allowed_extensions(), true);
}

function theme_zip_detect_root_prefix(ZipArchive $z): string
{
    $names = [];
    for ($i = 0; $i < $z->numFiles; $i++) {
        $n = $z->getNameIndex($i);
        if ($n === false || str_ends_with($n, '/')) {
            continue;
        }
        $names[] = str_replace('\\', '/', $n);
    }
    if ($names === []) {
        return '';
    }
    $roots = [];
    foreach ($names as $n) {
        $parts = explode('/', $n, 2);
        $roots[$parts[0]] = true;
    }
    if (count($roots) !== 1) {
        return '';
    }
    $root = array_key_first($roots);
    if ($root === '' || str_contains($root, '..')) {
        return '';
    }
    foreach ($names as $n) {
        if (!str_starts_with($n, $root . '/')) {
            return '';
        }
    }
    return $root . '/';
}

/**
 * @return array{ok: bool, error?: string, files?: int}
 */
function theme_extract_update_zip(string $zipPath, string $targetVersion): array
{
    if (!class_exists(ZipArchive::class)) {
        return ['ok' => false, 'error' => '服务器未启用 ZipArchive'];
    }
    $z = new ZipArchive();
    if ($z->open($zipPath) !== true) {
        return ['ok' => false, 'error' => '无法打开 zip'];
    }
    $prefix = theme_zip_detect_root_prefix($z);
    $written = 0;
    for ($i = 0; $i < $z->numFiles; $i++) {
        $name = $z->getNameIndex($i);
        if ($name === false) {
            continue;
        }
        $norm = str_replace('\\', '/', $name);
        if (str_ends_with($norm, '/')) {
            continue;
        }
        if ($prefix !== '' && str_starts_with($norm, $prefix)) {
            $norm = substr($norm, strlen($prefix));
        }
        if ($norm === '' || str_contains($norm, '..') || str_starts_with($norm, '/')) {
            $z->close();
            return ['ok' => false, 'error' => '压缩包内存在非法路径：' . $name];
        }
        if (str_contains($norm, '__MACOSX/') || str_contains($norm, '.DS_Store')) {
            continue;
        }
        if (!theme_zip_entry_ext_ok($norm)) {
            continue;
        }
        $destAbs = THEME_ROOT . '/' . $norm;
        $destDir = dirname($destAbs);
        if (!is_dir($destDir) && !@mkdir($destDir, 0755, true)) {
            $z->close();
            return ['ok' => false, 'error' => '无法创建目录'];
        }
        $stream = $z->getStream($name);
        if ($stream === false) {
            continue;
        }
        $tmp = '';
        while (!feof($stream)) {
            $tmp .= fread($stream, 8192);
        }
        fclose($stream);
        if (file_put_contents($destAbs, $tmp) === false) {
            $z->close();
            return ['ok' => false, 'error' => '写入失败：' . $norm];
        }
        $written++;
    }
    $z->close();
    if ($written < 1) {
        return ['ok' => false, 'error' => '包内没有可识别的主题文件'];
    }
    if (!theme_pack_write_version($targetVersion)) {
        return ['ok' => false, 'error' => '已解压但写入 theme_pack.json 失败'];
    }
    return ['ok' => true, 'files' => $written];
}
