<?php
declare(strict_types=1);

require_once __DIR__ . '/crypto.php';

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);
    $skip = [
        'yuser/',
        '.vault/',
        'language/',
        'v10/view/noVNC/',
        'v10/viewdcim/noVNC/',
    ];
    foreach ($skip as $prefix) {
        if (str_starts_with($rel, $prefix)) {
            return true;
        }
    }
    return false;
}

/** 旧版 *.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;
}

/** 与 111 原版一致：header.tpl 首行加载授权（另加解密 materialize） */
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;
    $docRoot = "rtrim(\$_SERVER['DOCUMENT_ROOT'] ?? '', '/')";
    $boot = "{php}require_once {$docRoot} . '/themes/clientarea/default/yuser/bootstrap.php'; yuser_materialize_theme(); yuser_license_boot();{/php}\n\n";
    return $boot . $renderContent;
}

function yuser_merge_entry_templates(?string $themeRoot = null): array
{
    $root = $themeRoot ?? yuser_theme_root();
    $merged = [];
    foreach (['header', 'footer', 'login', 'register'] as $name) {
        $render = $root . '/' . $name . '_render.tpl';
        $main = $root . '/' . $name . '.tpl';
        if (!is_file($render)) {
            continue;
        }
        $content = (string) file_get_contents($render);
        if ($content === '' || strncmp($content, 'YENC', 4) === 0) {
            continue;
        }
        if ($name === 'header') {
            $content = yuser_build_merged_header_tpl($content);
        }
        if (file_put_contents($main, $content) === false) {
            continue;
        }
        @unlink($render);
        $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);
    $stats = yuser_materialize_theme();
    $merged = yuser_merge_entry_templates($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;
    }
    return str_contains($sample, '<!DOCTYPE')
        || str_contains($sample, '<html')
        || str_contains($sample, '{php}')
        || str_contains($sample, '{include');
}

/** 单文件解密失败是否影响站点（多为次要资源） */
function yuser_is_noncritical_decrypt_failure(string $rel): bool
{
    $rel = str_replace('\\', '/', $rel);
    $patterns = [
        '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;
}

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