<?php
declare(strict_types=1);

require_once __DIR__ . '/crypto.php';

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;
    }
}

if (!function_exists('str_ends_with')) {
    function str_ends_with(string $haystack, string $needle): bool
    {
        $len = strlen($needle);
        return $needle === '' || ($len <= strlen($haystack) && substr($haystack, -$len) === $needle);
    }
}

function yuser_theme_root(): string
{
    if (!empty($GLOBALS['YUSER_THEME_ROOT_OVERRIDE']) && is_dir($GLOBALS['YUSER_THEME_ROOT_OVERRIDE'])) {
        return rtrim((string) $GLOBALS['YUSER_THEME_ROOT_OVERRIDE'], '/\\');
    }
    return dirname(__DIR__);
}

function yuser_vault_dir(): string
{
    return yuser_theme_root() . '/.vault';
}

/** 不参与加密的相对路径前缀（正斜杠） */
function yuser_encrypt_skip_relative(string $rel): bool
{
    $rel = str_replace('\\', '/', $rel);
    if ($rel === '.htaccess' || str_ends_with($rel, '/.htaccess')) {
        return true;
    }
    $skip = [
        'yuser/',
        '.vault/',
        'language/',
        'v10/view/noVNC/',
        'v10/viewdcim/noVNC/',
    ];
    foreach ($skip as $prefix) {
        if (str_starts_with($rel, $prefix)) {
            return true;
        }
    }
    return false;
}

/** 安装已完成：header 已合并为完整 HTML，无需每次全量解密 .vault */
function yuser_theme_installed_ready(string $root): bool
{
    $header = $root . '/header.tpl';
    if (!is_file($header) || !is_file($root . '/license/bootstrap.php')) {
        return false;
    }
    $sample = (string) file_get_contents($header, false, null, 0, 512);
    if ($sample === '' || strncmp($sample, 'YENC', 4) === 0) {
        return false;
    }
    if (str_contains($sample, 'header_render.tpl')) {
        return false;
    }
    return str_contains($sample, '<!DOCTYPE') || str_contains($sample, '<html');
}

/**
 * 客户区运行时入口（与 111 一致：已安装则只校验授权，不每次扫描 626 个 vault 文件）。
 */
function yuser_runtime_theme_boot(): void
{
    $root = yuser_theme_root();
    if (yuser_theme_installed_ready($root)) {
        yuser_require_license_or_exit();
        yuser_license_boot();
        return;
    }
    yuser_materialize_theme(true);
    yuser_license_boot();
}

/** 旧版 *.tpl.yenc / *.js.yenc 等（易被子模板引擎误读） */
function yuser_legacy_yenc_plain_path(string $yencPath, string $root): ?string
{
    $rel = str_replace('\\', '/', substr($yencPath, strlen($root) + 1));
    if (!str_ends_with(strtolower($rel), '.yenc')) {
        return null;
    }
    $base = substr($rel, 0, -5);
    $exts = ['.tpl', '.js', '.css', '.php'];
    foreach ($exts as $ext) {
        if (str_ends_with(strtolower($base), $ext)) {
            return $root . '/' . str_replace('/', DIRECTORY_SEPARATOR, $base);
        }
    }
    return null;
}

/** 未授权时禁止解密 .vault（安装页在 install.php 内单独校验） */
function yuser_require_license_or_exit(string $blockedTitle = '模板未授权'): void
{
    $vf = __DIR__ . '/license_verify.php';
    if (!is_file($vf)) {
        http_response_code(500);
        header('Content-Type: text/html; charset=utf-8');
        echo '<p>缺少授权模块 license_verify.php</p>';
        exit;
    }
    require_once $vf;
    $lic = yuser_license_assert_authorized();
    if (!empty($lic['authorized'])) {
        return;
    }
    yuser_license_render_install_blocked($lic, $blockedTitle);
    exit;
}

/**
 * 将 .vault 或旧版 .yenc 解密为明文模板文件（须已授权）。
 *
 * @return array{files: int, errors: int}
 */
function yuser_materialize_theme(bool $requireLicense = true): array
{
    static $done = false;
    if ($done) {
        return [
            'files' => 0,
            'errors' => 0,
            'failed' => is_array($GLOBALS['YUSER_DECRYPT_FAILED'] ?? null) ? $GLOBALS['YUSER_DECRYPT_FAILED'] : [],
        ];
    }

    if ($requireLicense) {
        yuser_require_license_or_exit();
    }

    $GLOBALS['YUSER_DECRYPT_FAILED'] = [];
    $root = yuser_theme_root();
    $stats = ['files' => 0, 'errors' => 0, 'failed' => []];
    if (!is_dir($root)) {
        $done = true;
        return $stats;
    }

    $vault = yuser_vault_dir();
    if (is_dir($vault)) {
        $it = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($vault, FilesystemIterator::SKIP_DOTS)
        );
        foreach ($it as $file) {
            if (!$file->isFile()) {
                continue;
            }
            $vaultPath = $file->getPathname();
            $rel = substr($vaultPath, strlen($vault) + 1);
            $rel = str_replace('\\', '/', $rel);
            if (yuser_encrypt_skip_relative($rel)) {
                continue;
            }
            $plainPath = $root . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $rel);
            if (yuser_write_plain_from_encrypted($vaultPath, $plainPath)) {
                $stats['files']++;
            } else {
                $stats['errors']++;
                $stats['failed'][] = $rel;
                $GLOBALS['YUSER_DECRYPT_FAILED'][] = $rel;
            }
        }
    }

    $legacyIt = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS)
    );
    foreach ($legacyIt as $file) {
        if (!$file->isFile()) {
            continue;
        }
        $path = $file->getPathname();
        if (!str_ends_with(strtolower($path), '.yenc')) {
            continue;
        }
        $rel = substr($path, strlen($root) + 1);
        if (yuser_encrypt_skip_relative(str_replace('\\', '/', $rel))) {
            continue;
        }
        $plainPath = yuser_legacy_yenc_plain_path($path, $root);
        if ($plainPath === null) {
            continue;
        }
        if (yuser_write_plain_from_encrypted($path, $plainPath)) {
            $stats['files']++;
        } else {
            $stats['errors']++;
            $relLegacy = str_replace('\\', '/', substr($path, strlen($root) + 1));
            $stats['failed'][] = $relLegacy;
            $GLOBALS['YUSER_DECRYPT_FAILED'][] = $relLegacy;
        }
    }

    $done = true;
    return $stats;
}

/**
 * 将 *_render.tpl 合并为入口 tpl（WHMCS 不支持 {php}，不能依赖 include render）。
 *
 * @return list<string> 已合并的文件名
 */
/** 加载授权（bootstrap 在 .vault 内，需先 materialize） */
function yuser_license_boot(): void
{
    $bootstrap = yuser_theme_root() . '/license/bootstrap.php';
    if (!is_file($bootstrap)) {
        http_response_code(403);
        header('Content-Type: text/html; charset=utf-8');
        echo '<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><title>模板未授权</title></head><body style="font-family:system-ui,sans-serif;padding:40px;text-align:center">';
        echo '<h1>模板未授权</h1><p>授权模块未就绪，请重新运行 <code>/install.php</code> 或 <code>/yuser_theme_unlock.php</code>。</p></body></html>';
        exit;
    }
    require_once $bootstrap;
}

/** 合并后 header：经 public/yuser_license_boot.php 加载（与 install.php 同级，路径最稳） */
function yuser_build_merged_header_tpl(string $renderContent): string
{
    $renderContent = preg_replace('/^\xEF\xBB\xBF/', '', $renderContent) ?? $renderContent;
    $renderContent = preg_replace('/^\{php\}.*?\{\/php\}\s*/s', '', $renderContent) ?? $renderContent;
    $license = <<<'SMARTY'
{php}
$_yuserBridge = rtrim($_SERVER['DOCUMENT_ROOT'] ?? '', '/') . '/yuser_license_boot.php';
if (!is_file($_yuserBridge)) {
    $_yuserBridge = rtrim($_SERVER['DOCUMENT_ROOT'] ?? '', '/') . '/public/yuser_license_boot.php';
}
require_once $_yuserBridge;
{/php}

SMARTY;
    return $license . $renderContent;
}

/** 安装后修正 header 首行（防止旧包仍写 yuser_materialize 导致 500） */
function yuser_fix_merged_header_license_line(?string $themeRoot = null): bool
{
    $root = $themeRoot ?? yuser_theme_root();
    $header = $root . '/header.tpl';
    if (!is_file($header)) {
        return false;
    }
    $content = (string) file_get_contents($header);
    if (!str_contains($content, 'yuser_materialize_theme')
        && !str_contains($content, 'yuser_runtime_theme_boot')
        && !str_contains($content, 'yuser_load_license_bootstrap')
        && !str_contains($content, 'yuser_license_boot.php')) {
        return false;
    }
    $render = preg_replace('/^\{php\}.*?\{\/php\}\s*/s', '', $content) ?? $content;
    $fixed = yuser_build_merged_header_tpl($render);
    return file_put_contents($header, $fixed) !== false;
}

/**
 * 修复 header.tpl 内 request()->url()（Smarty {php} 下会破坏 ThinkPHP，导致 500）。
 */
function yuser_patch_header_thinkphp_compat(?string $themeRoot = null): bool
{
    $header = ($themeRoot ?? yuser_theme_root()) . '/header.tpl';
    if (!is_file($header)) {
        return false;
    }
    $c = (string) file_get_contents($header);
    if (!str_contains($c, 'request()->url()')) {
        return false;
    }
    $c = preg_replace(
        '/\$parse\s*=\s*parse_url\s*\(\s*request\(\)->url\(\)\s*\)\s*;\s*\$path\s*=\s*\$parse\[[\'"]path[\'"]\]\s*;\s*\$query\s*=\s*\$parse\[[\'"]query[\'"]\]\s*;/s',
        "\$parse = parse_url((string) (\$_SERVER['REQUEST_URI'] ?? '/'));\n\t\t\t\t\t\t\t\t\$path = \$parse['path'] ?? '/';\n\t\t\t\t\t\t\t\t\$query = \$parse['query'] ?? '';",
        $c
    ) ?? $c;
    $c = str_replace(
        "{php}\$hooks=hook('client_area_head_output');{/php}",
        "{php}\$hooks = function_exists('hook') ? hook('client_area_head_output') : [];" . "{/php}",
        $c
    );
    return file_put_contents($header, $c) !== false;
}

/** 误解密到主题根目录的 .htaccess 会导致整站 500，安装后删除 */
function yuser_cleanup_theme_htaccess(?string $themeRoot = null): bool
{
    $ht = ($themeRoot ?? yuser_theme_root()) . '/.htaccess';
    if (!is_file($ht)) {
        return false;
    }
    $head = (string) file_get_contents($ht, false, null, 0, 16);
    if (strncmp($head, 'YENC', 4) === 0 || strncmp($head, '<If', 2) !== 0) {
        return @unlink($ht);
    }
    return false;
}

/**
 * 读取 *_render.tpl（明文或直接从 .vault 解密，避免合并失败留下 include 导致 500）。
 */
function yuser_read_render_tpl_content(string $root, string $name): ?string
{
    $render = $root . '/' . $name . '_render.tpl';
    if (is_file($render)) {
        $content = (string) file_get_contents($render);
        if ($content !== '' && strncmp($content, 'YENC', 4) !== 0) {
            return $content;
        }
    }
    $vaultRender = $root . '/.vault/' . $name . '_render.tpl';
    if (!is_file($vaultRender)) {
        return null;
    }
    try {
        $plain = yuser_decrypt_bytes((string) file_get_contents($vaultRender));
    } catch (Throwable $e) {
        return null;
    }
    if ($plain === '' || strncmp($plain, 'YENC', 4) === 0) {
        return null;
    }
    @file_put_contents($render, $plain);
    return $plain;
}

function yuser_merge_entry_templates(?string $themeRoot = null): array
{
    $root = $themeRoot ?? yuser_theme_root();
    $merged = [];
    foreach (['header', 'footer', 'login', 'register'] as $name) {
        $main = $root . '/' . $name . '.tpl';
        $content = yuser_read_render_tpl_content($root, $name);
        if ($content === null) {
            continue;
        }
        if ($name === 'header') {
            $content = yuser_build_merged_header_tpl($content);
        }
        if (file_put_contents($main, $content) === false) {
            continue;
        }
        @unlink($root . '/' . $name . '_render.tpl');
        $merged[] = $name . '.tpl';
    }
    return $merged;
}

/** 删除主题目录内遗留的旧版 *.yenc（避免被误当作模板输出） */
function yuser_cleanup_legacy_yenc(?string $themeRoot = null): int
{
    $root = $themeRoot ?? yuser_theme_root();
    $removed = 0;
    if (!is_dir($root)) {
        return 0;
    }
    $it = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS)
    );
    foreach ($it as $file) {
        if (!$file->isFile()) {
            continue;
        }
        $path = $file->getPathname();
        if (str_ends_with(strtolower($path), '.yenc')) {
            @unlink($path);
            $removed++;
        }
    }
    return $removed;
}

function yuser_ensure_htaccess_vault(?string $themeRoot = null): void
{
    $vault = ($themeRoot ?? yuser_theme_root()) . '/.vault';
    if (!is_dir($vault)) {
        return;
    }
    $ht = $vault . '/.htaccess';
    $body = "<IfModule mod_authz_core.c>\n    Require all denied\n</IfModule>\n<IfModule !mod_authz_core.c>\n    Deny from all\n</IfModule>\n";
    if (!is_file($ht)) {
        @file_put_contents($ht, $body);
    }
}

/**
 * 安装 / 急救：解密 .vault → 合并入口模板 → 清理遗留 .yenc
 *
 * @return array{files: int, errors: int, merged: list<string>, yenc_removed: int}
 */
function yuser_theme_install_unlock(?string $themeRoot = null): array
{
    if ($themeRoot !== null) {
        // 临时让 materialize 使用指定目录（install 阶段路径最准确）
        $GLOBALS['YUSER_THEME_ROOT_OVERRIDE'] = $themeRoot;
    }
    yuser_ensure_htaccess_vault($themeRoot);
    // 安装流程已在 install.php 校验授权，此处不再重复拦截
    $stats = yuser_materialize_theme(false);
    $merged = yuser_merge_entry_templates($themeRoot);
    yuser_fix_merged_header_license_line($themeRoot);
    yuser_patch_header_thinkphp_compat($themeRoot);
    yuser_cleanup_theme_htaccess($themeRoot);
    $yencRemoved = yuser_cleanup_legacy_yenc($themeRoot);
    unset($GLOBALS['YUSER_THEME_ROOT_OVERRIDE']);
    return [
        'files' => $stats['files'],
        'errors' => $stats['errors'],
        'failed' => $stats['failed'] ?? [],
        'merged' => $merged,
        'yenc_removed' => $yencRemoved,
    ];
}

function yuser_write_plain_from_encrypted(string $encPath, string $plainPath): bool
{
    if (is_file($plainPath) && filemtime($plainPath) >= filemtime($encPath)) {
        return true;
    }
    try {
        $plain = yuser_decrypt_bytes((string) file_get_contents($encPath));
    } catch (Throwable $e) {
        return false;
    }
    $dir = dirname($plainPath);
    if (!is_dir($dir) && !@mkdir($dir, 0755, true) && !is_dir($dir)) {
        return false;
    }
    return file_put_contents($plainPath, $plain) !== false;
}

function yuser_theme_is_ready(?string $themeRoot = null): bool
{
    $root = $themeRoot ?? yuser_theme_root();
    $header = $root . '/header.tpl';
    if (!is_file($header) || filesize($header) < 200) {
        return false;
    }
    $sample = (string) file_get_contents($header, false, null, 0, 512);
    if (strncmp($sample, 'YENC', 4) === 0) {
        return false;
    }
    // 安装后须已合并为完整 HTML，不能仍是仅 include header_render 的桩
    if (str_contains($sample, 'header_render.tpl')) {
        return false;
    }
    return str_contains($sample, '<!DOCTYPE')
        || str_contains($sample, '<html')
        || str_contains($sample, '{php}');
}

/** 单文件解密失败是否影响站点（多为次要资源） */
function yuser_is_noncritical_decrypt_failure(string $rel): bool
{
    $rel = str_replace('\\', '/', $rel);
    $patterns = [
        '.htaccess',
        'assets/libs/parsleyjs/i18n/',
        'assets/libs/summernote/plugin/',
        'assets/libs/bootstrap-rating/',
        'assets/libs/bootstrap-touchspin/',
        'assets/libs/moment/ender.js',
        'logout.tpl',
        'knowledgebaselist.tpl',
        'downloadscate.tpl',
        'error/notifications.tpl',
        'includes/breadcrumb.tpl',
    ];
    foreach ($patterns as $p) {
        if (str_contains($rel, $p)) {
            return true;
        }
    }
    return false;
}
