<?php
// +----------------------------------------------------------------------
// | onezan-admin 快速开发的管理平台
// +----------------------------------------------------------------------
// | 官方网址：https://www.onezan.cn
// +----------------------------------------------------------------------
// | onezan团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: 543885307@qq.com
// +----------------------------------------------------------------------
declare(strict_types=1);

if (!function_exists('onezan_load_php_tree')) {

    function onezan_load_php_tree(array $paths, array $excludeBaseNames = []): void
    {
        foreach ($paths as $path) {
            if (!$path || !file_exists($path)) {
                continue;
            }

            if (is_file($path)) {
                if (pathinfo($path, PATHINFO_EXTENSION) !== 'php') {
                    continue;
                }
                if (in_array(basename($path), $excludeBaseNames, true)) {
                    continue;
                }

                require $path;
                continue;
            }

            $iterator = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)
            );

            $files = [];
            foreach ($iterator as $file) {
                if (!$file->isFile() || $file->getExtension() !== 'php') {
                    continue;
                }
                if (in_array($file->getFilename(), $excludeBaseNames, true)) {
                    continue;
                }

                $files[] = $file->getPathname();
            }

            sort($files, SORT_STRING);
            foreach ($files as $filePath) {
                require $filePath;
            }
        }
    }
}

if (!function_exists('onezan_package_route_paths')) {

    function onezan_package_route_paths(string $rootPath, string $channel): array
    {
        $patterns = [
            $rootPath . DIRECTORY_SEPARATOR . 'applications' . DIRECTORY_SEPARATOR . '*' . DIRECTORY_SEPARATOR . 'route' . DIRECTORY_SEPARATOR . $channel . '.php',
            $rootPath . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . '*' . DIRECTORY_SEPARATOR . 'route' . DIRECTORY_SEPARATOR . $channel . '.php',
            $rootPath . DIRECTORY_SEPARATOR . 'applications' . DIRECTORY_SEPARATOR . '*' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . '*' . DIRECTORY_SEPARATOR . 'route' . DIRECTORY_SEPARATOR . $channel . '.php',
        ];

        $paths = [];
        foreach ($patterns as $pattern) {
            foreach (glob($pattern) ?: [] as $matchedPath) {
                if (is_file($matchedPath)) {
                    $paths[] = $matchedPath;
                }
            }
        }

        $paths = array_values(array_unique($paths));
        sort($paths, SORT_STRING);

        return $paths;
    }
}

if (!function_exists('onezan_load_package_routes')) {

    function onezan_load_package_routes(string $rootPath, string $channel, array $excludeBaseNames = []): void
    {
        $paths = onezan_package_route_paths($rootPath, $channel);

        // TODO: Route cache needs to distinguish bridge files from leaf definition files
        // before enabling in production. See onezan_compile_routes().
        // if (onezan_try_route_cache($channel, $paths)) {
        //     return;
        // }

        onezan_load_php_tree($paths, $excludeBaseNames);
    }
}

if (!function_exists('onezan_try_route_cache')) {
    /**
     * Try loading compiled route cache.
     * Returns true if cache was loaded, false if we should fall through to filesystem scan.
     */
    function onezan_try_route_cache(string $channel, array $paths): bool
    {
        $cacheFile = onezan_route_cache_path($channel);
        if (!$cacheFile || !file_exists($cacheFile)) {
            return false;
        }

        // Invalidate if any source file is newer than cache
        $cacheMtime = filemtime($cacheFile);
        foreach ($paths as $path) {
            if (file_exists($path) && filemtime($path) > $cacheMtime) {
                return false;
            }
        }

        require $cacheFile;
        return true;
    }
}

if (!function_exists('onezan_route_cache_path')) {
    function onezan_route_cache_path(string $channel): string
    {
        try {
            $runtime = app()->getRuntimePath();
            return $runtime . 'route_' . $channel . '_compiled.php';
        } catch (\Throwable $e) {
            return '';
        }
    }
}

if (!function_exists('onezan_compile_routes')) {
    /**
     * Compile all route files into a single cache file.
     * Call via CLI: php think route:cache
     * Returns number of files compiled.
     */
    function onezan_compile_routes(string $rootPath): int
    {
        $compiled = 0;
        foreach (['admin', 'app'] as $channel) {
            $paths = onezan_package_route_paths($rootPath, $channel);
            if (empty($paths)) continue;

            $cacheFile = onezan_route_cache_path($channel);
            if (!$cacheFile) continue;

            $content = "<?php\n// Route cache compiled at " . date('Y-m-d H:i:s') . " ({$channel})\n";
            $first = true;
            foreach ($paths as $path) {
                $src = php_strip_whitespace($path);
                if ($src) {
                    $content .= "\n// source: " . basename(dirname($path)) . '/' . basename($path) . "\n";
                    // Strip opening <?php tag
                    $src = preg_replace('/^<\?php\s*/i', '', $src);
                    if (!$first) {
                        // Strip declare(strict_types=1)
                        $src = preg_replace('/\bdeclare\s*\(\s*strict_types\s*=\s*1\s*\)\s*;?\s*/i', '', $src);
                        // Strip use statements (already imported by first file)
                        $src = preg_replace('/\buse\s+[a-zA-Z_\\\]+(?:\s+as\s+[a-zA-Z_]+)?\s*;(\s*\n?)*/', '', $src);
                        // Strip require_once / require (files already merged)
                        $src = preg_replace('/\b(require_once|require|include_once|include)\s*\(?\s*[^;]+\s*\)?\s*;(\s*\n?)*/', '', $src);
                    }
                    $first = false;
                    $content .= $src;
                    $compiled++;
                }
            }

            $dir = dirname($cacheFile);
            if (!is_dir($dir)) mkdir($dir, 0755, true);
            file_put_contents($cacheFile, $content, LOCK_EX);

            if (function_exists('opcache_invalidate')) {
                opcache_invalidate($cacheFile, true);
            }
        }
        return $compiled;
    }
}