<?php
namespace Core;

/**
 * 应用核心：自动加载、配置、路由、分发
 */
class App
{
    public static $config = [];

    /** 初始化：辅助函数、配置、会话、自动加载（install 脚本可复用） */
    public static function init()
    {
        // 0. 全局辅助函数
        require_once BASE_PATH . '/app/Core/Helper.php';

        // 1. 时区 & 会话
        date_default_timezone_set(self::config('app.timezone', 'Asia/Shanghai'));
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start([
                'cookie_httponly' => true,
                'cookie_samesite' => 'Lax',
                'cookie_secure'   => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'), // HTTPS 下仅安全传输
            ]);
        }

        // 2. 自动加载（Core / App\Controllers / App\Models）
        spl_autoload_register(function ($class) {
            $prefixes = [
                'Core\\'            => BASE_PATH . '/app/Core/',
                'App\\Controllers\\' => BASE_PATH . '/app/Controllers/',
                'App\\Models\\'      => BASE_PATH . '/app/Models/',
            ];
            foreach ($prefixes as $prefix => $base) {
                if (strncmp($class, $prefix, strlen($prefix)) === 0) {
                    $rel = substr($class, strlen($prefix));
                    $file = $base . str_replace('\\', '/', $rel) . '.php';
                    if (is_file($file)) { require $file; return true; }
                }
            }
            return false;
        });
    }

    public static function run()
    {
        self::init();
        self::dispatch(self::parseRoute());
    }

    /** 解析请求路径为段数组 */
    public static function parseRoute(): array
    {
        $uri = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
        // 仅当 SCRIPT_NAME 真实以 index.php 结尾时（Apache/生产模式）才剥离目录前缀；
        // 内置服务器路由模式下 SCRIPT_NAME 等于请求路径，此时不剥离，避免误删 admin 等段。
        $script = $_SERVER['SCRIPT_NAME'] ?? '/index.php';
        if (substr($script, -strlen('/index.php')) === '/index.php') {
            $dir = dirname($script);
            if ($dir !== '/' && strpos($uri, $dir) === 0) {
                $uri = substr($uri, strlen($dir));
            }
        }
        $uri = preg_replace('#/index\.php$#i', '', $uri);
        $uri = trim($uri, '/');
        if ($uri === '') return [];
        return explode('/', $uri);
    }

    /** 路由分发 */
    public static function dispatch(array $segments)
    {
        // 静态资源直出
        $asset = BASE_PATH . '/public/' . implode('/', $segments);
        if ($segments && is_file($asset) && !is_dir($asset)) {
            self::serveFile($asset);
            return;
        }

        $admin = self::config('app.admin_path', 'admin');
        if (!empty($segments) && $segments[0] === $admin) {
            self::dispatchAdmin(array_slice($segments, 1));
            return;
        }
        // 分系统入口：CRM 客户管理 / PSI 进销存（统一受 super_admin 与分系统权限管辖）
        // 大小写不敏感：/crm、/CRM、/psi、/PSI 均可进入，避免因 URL 大小写不同导致 404
        if (!empty($segments)) {
            $seg0 = strtoupper($segments[0]);
            if ($seg0 === 'CRM' || $seg0 === 'PSI') {
                self::dispatchSubsys($seg0, array_slice($segments, 1));
                return;
            }
        }
        self::dispatchFront($segments);
    }

    private static function dispatchSubsys(string $raw, array $s)
    {
        $sys = strtoupper($raw);                 // CRM / PSI
        $sysKey = $sys === 'PSI' ? 'psi' : 'crm';
        // 未登录或无该系统角色：拦截（拥有 crm/psi 任意角色即可进入；写操作由各 Controller 的 subsys_admin 二次把关）
        if (!subsys_can_enter($sysKey)) {
            self::forbidden('您没有访问「' . $sys . '」系统的权限');
            return;
        }
        // 统一入口：CRM\DashboardController / PSI\DashboardController 内部再做子路由
        $ctrl = 'App\\Controllers\\' . $sys . '\\DashboardController';
        $action = 'dispatch';
        if (!class_exists($ctrl)) { self::notFound("系统不存在: $sys"); return; }
        $instance = new $ctrl();
        if (!method_exists($instance, $action)) { self::notFound("入口不存在: $action"); return; }
        try {
            $html = call_user_func_array([$instance, $action], [$s]);
        } catch (\Throwable $e) {
            // 子系统异常兜底：保留左导 + 右框，框内显示错误，绝不白屏
            error_log('Subsys[' . $sys . '] error: ' . $e->getMessage());
            $html = self::subsysErrorFrame($sysKey, $instance, $e);
        }
        if (is_string($html)) echo $html;
    }

    private static function dispatchFront(array $s)
    {
        $key = $s[0] ?? '';
        $seg2 = $s[1] ?? null;

        // 验证码图片输出（独立端点，直接输出 PNG）
        if ($key === 'captcha' && $seg2 === 'image') {
            captcha_image();
            return;
        }

        // Sitemap 动态生成（XML 格式，搜索引擎自动抓取）
        if ($key === 'sitemap.xml') {
            self::outputSitemap();
            return;
        }

        // 资源详情路由（带第二段 slug）
        if (($key === 'product' || $key === 'products') && $seg2) {
            self::call('App\\Controllers\\ProductController', 'show', [$seg2]);
            return;
        }
        if ($key === 'news' && $seg2) {
            self::call('App\\Controllers\\NewsController', 'show', [$seg2]);
            return;
        }
        if ($key === 'cases' && $seg2) {
            self::call('App\\Controllers\\CaseController', 'show', [$seg2]);
            return;
        }

        // 订单 / 支付（含网关异步通知，无需登录）
        if ($key === 'order') {
            $action = $s[1] ?? 'checkout';
            $param = $s[2] ?? null;
            self::call('App\\Controllers\\OrderController', $action, [$param]);
            return;
        }
        if ($key === 'pay') {
            $action = $s[1] ?? 'notify';
            $param = $s[2] ?? null;
            self::call('App\\Controllers\\PayController', $action, [$param]);
            return;
        }

        $map = [
            ''         => ['HomeController', 'index'],
            'home'     => ['HomeController', 'index'],
            'products' => ['ProductController', 'index'],
            'news'     => ['NewsController', 'index'],
            'cases'    => ['CaseController', 'index'],
            'page'     => ['PageController', 'show'],
            'contact'  => ['ContactController', 'index'],
        ];
        if (isset($map[$key])) {
            [$ctrl, $action] = $map[$key];
            $param = $seg2;
            self::call('App\\Controllers\\' . $ctrl, $action, [$param]);
            return;
        }
        // 未知路径：按单页 slug 处理（/about、/service ...）
        self::call('App\\Controllers\\PageController', 'show', [$key]);
    }

    private static function dispatchAdmin(array $s)
    {
        $res = $s[0] ?? '';
        $action = $s[1] ?? 'index';
        $id = $s[2] ?? null;
        $map = [
            ''          => ['Admin\\DashboardController', 'index'],
            'dashboard' => ['Admin\\DashboardController', 'index'],
            'login'     => ['Admin\\AuthController', 'login'],
            'logout'    => ['Admin\\AuthController', 'logout'],
            'password'  => ['Admin\\AuthController', 'password'],
            'products'  => ['Admin\\ProductController', 'index'],
            'categories'=> ['Admin\\CategoryController', 'index'],
            'news'      => ['Admin\\NewsController', 'index'],
            'pages'     => ['Admin\\PageController', 'index'],
            'banners'   => ['Admin\\BannerController', 'index'],
            'settings'  => ['Admin\\SettingController', 'index'],
            'theme'     => ['Admin\\SettingController', 'theme'],
            'seo'       => ['Admin\\SettingController', 'seo'],
            'users'     => ['Admin\\UserController', 'index'],
            'system'    => ['Admin\\SystemController', 'index'],
            'orders'    => ['Admin\\OrderController', 'index'],
            'payments'  => ['Admin\\SettingController', 'payment'],
            'cases'     => ['Admin\\CaseController', 'index'],
            'media'     => ['Admin\\MediaController', 'index'],
            'upgrade'   => ['Admin\\UpgradeController', 'index'],
            'db'        => ['Admin\\DatabaseController', 'index'],
        ];
        if (!isset($map[$res])) {
            self::notFound();
            return;
        }
        // 权限能力检查：未登录或权限不足直接拦截（登录态由对应控制器再兜底）
        $capMap = [
            'users'     => 'users',
            'system'    => 'users',
            'upgrade'   => 'users',
            'db'        => 'users',
            'settings'  => 'settings',
            'theme'     => 'settings',
            'seo'       => 'settings',
            'payments'  => 'settings',
            'products'  => 'products',
            'categories'=> 'categories',
            'news'      => 'news',
            'pages'     => 'pages',
            'banners'   => 'banners',
            'orders'    => 'orders',
            'cases'     => 'cases',
            'media'     => 'pages',
        ];
        if (isset($capMap[$res]) && !admin_can($capMap[$res])) {
            self::forbidden('当前账号无访问「' . $res . '」的权限');
            return;
        }
        [$ctrl, $default] = $map[$res];
        $method = ($action === 'index') ? $default : $action;
        self::call('App\\Controllers\\' . $ctrl, $method, [$id]);
    }

    private static function call($class, $method, array $args = [])
    {
        if (!class_exists($class)) { self::notFound("类不存在: $class"); return; }
        $instance = new $class();
        if (!method_exists($instance, $method)) { self::notFound("方法不存在: $method"); return; }
        $html = call_user_func_array([$instance, $method], $args);
        if (is_string($html)) echo $html;
    }

    public static function notFound($msg = '')
    {
        http_response_code(404);
        echo '<!doctype html><meta charset=utf-8><title>404</title>
        <style>body{font-family:system-ui;display:grid;place-items:center;height:100vh;margin:0;background:#0f172a;color:#e2e8f0}
        .b{text-align:center}.c{color:#38bdf8;font-size:64px;font-weight:800;margin:0}</style>
        <div class="b"><p class="c">404</p><p>页面不存在' . ($msg ? '：' . htmlspecialchars($msg) : '') . '</p>
        <p><a style="color:#38bdf8" href="' . site_url() . '">返回首页</a></p></div>';
    }

    public static function forbidden(string $msg = '')
    {
        http_response_code(403);
        echo '<!doctype html><meta charset=utf-8><title>403</title>
        <style>body{font-family:system-ui;display:grid;place-items:center;height:100vh;margin:0;background:#0f172a;color:#e2e8f0}
        .b{text-align:center}.c{color:#f87171;font-size:56px;font-weight:800;margin:0}</style>
        <div class="b"><p class="c">403</p><p>无访问权限' . ($msg ? '：' . htmlspecialchars($msg) : '') . '</p>
        <p><a style="color:#38bdf8" href="' . site_url('admin') . '">返回后台</a></p></div>';
    }

    /**
     * 子系统异常兜底框：左导 + 右框保持完整，框内显示错误信息，绝不白屏。
     */
    private static function subsysErrorFrame(string $sysKey, $instance, \Throwable $e): string
    {
        $nav = method_exists($instance, 'nav') ? $instance->nav('') : [];
        $content = '<div class="page-head"><h1>页面加载出错</h1>'
            . '<p class="sub">系统遇到问题，错误已记录，请稍后重试或联系管理员</p></div>'
            . '<div class="alert alert-err">' . e('错误信息：' . $e->getMessage()) . '</div>';
        return \Core\View::make('layouts/subsys', [
            'content' => $content,
            '_sys'    => $sysKey,
            '_nav'    => $nav,
            '_seg'    => '',
        ]);
    }

    private static function serveFile($file)
    {
        $real = realpath($file);
        $pub  = realpath(BASE_PATH . '/public');
        if ($real === false || $pub === false || strpos($real, $pub . DIRECTORY_SEPARATOR) !== 0 || !is_file($real)) {
            self::notFound('非法文件访问');
            return;
        }
        $mime = mime_content_type($real);
        header('Content-Type: ' . $mime);
        header('Content-Length: ' . filesize($real));
        readfile($real);
        exit;
    }

    /** 读取配置 config('app.name') */
    /**
     * 生成站内 URL（兼容子目录部署）。
     * 用法：App::url('PSI/reminders') -> https://host/base/PSI/reminders
     */
    public static function url(string $path = ''): string
    {
        return site_url($path);
    }

    public static function config(string $key, $default = null)
    {
        if (empty(self::$config)) {
            self::$config = require BASE_PATH . '/config/config.php';
        }
        $keys = explode('.', $key);
        $v = self::$config;
        foreach ($keys as $k) {
            if (!is_array($v) || !array_key_exists($k, $v)) return $default;
            $v = $v[$k];
        }
        return $v;
    }

    /** 动态生成 Sitemap XML（Google/Bing/Baidu 自动抓取） */
    private static function outputSitemap(): void
    {
        header('Content-Type: application/xml; charset=utf-8');
        echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
        echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";

        // 首页
        echo '<url><loc>' . e(site_url()) . '</loc><priority>1.0</priority><changefreq>daily</changefreq></url>' . "\n";

        // 静态页面
        $staticPages = [
            ['url' => site_url('products'), 'prio' => '0.9', 'freq' => 'weekly'],
            ['url' => site_url('news'),     'prio' => '0.8', 'freq' => 'daily'],
            ['url' => site_url('cases'),    'prio' => '0.8', 'freq' => 'weekly'],
            ['url' => site_url('page/about'), 'prio' => '0.7', 'freq' => 'monthly'],
            ['url' => site_url('contact'),  'prio' => '0.7', 'freq' => 'monthly'],
        ];
        foreach ($staticPages as $p) {
            echo '<url><loc>' . e($p['url']) . '</loc><priority>' . $p['prio'] . '</priority><changefreq>' . $p['freq'] . '</changefreq></url>' . "\n";
        }

        // 动态页面列表：产品 / 新闻 / 案例 / 单页
        $models = [
            ['class' => 'App\\Models\\Product', 'method' => 'all', 'urlPrefix' => 'product/', 'prio' => '0.85', 'freq' => 'weekly'],
            ['class' => 'App\\Models\\News',    'method' => 'published', 'urlPrefix' => 'news/', 'prio' => '0.75', 'freq' => 'weekly'],
            ['class' => 'App\\Models\\CustomerCase', 'method' => 'published', 'urlPrefix' => 'cases/', 'prio' => '0.75', 'freq' => 'weekly'],
            ['class' => 'App\\Models\\Page',     'method' => 'all', 'urlPrefix' => 'page/', 'prio' => '0.6', 'freq' => 'monthly'],
        ];

        foreach ($models as $m) {
            if (!class_exists($m['class'])) continue;
            try {
                $instance = new $m['class']();
                $items = [];
                if ($m['method'] === 'all') {
                    $items = $instance->all();
                } elseif ($m['method'] === 'published') {
                    $items = $instance->published(200);
                }
                foreach ($items as $item) {
                    $slug = $item['slug'] ?? ($item['id'] ?? '');
                    if (empty($slug)) continue;
                    $url = site_url($m['urlPrefix'] . $slug);
                    $lastmod = '';
                    if (!empty($item['updated_at'])) {
                        $lastmod = '<lastmod>' . e($item['updated_at']) . '</lastmod>';
                    } elseif (!empty($item['created_at'])) {
                        $lastmod = '<lastmod>' . e($item['created_at']) . '</lastmod>';
                    }
                    echo '<url><loc>' . e($url) . '</loc>' . $lastmod . '<priority>' . $m['prio'] . '</priority><changefreq>' . $m['freq'] . '</changefreq></url>' . "\n";
                }
            } catch (\Throwable $e) {
                // 静默跳过异常的模型，保证 sitemap 完整性
                continue;
            }
        }

        // 产品分类页（/products?cat=ID）
        if (class_exists('App\\Models\\Category')) {
            try {
                $catM = new \App\Models\Category();
                foreach ($catM->all() as $c) {
                    $cid = $c['id'] ?? 0;
                    if (!$cid) continue;
                    $url = site_url('products?cat=' . $cid);
                    echo '<url><loc>' . e($url) . '</loc><priority>0.7</priority><changefreq>weekly</changefreq></url>' . "\n";
                }
            } catch (\Throwable $e) {
                // 忽略
            }
        }

        echo '</urlset>';
        exit;
    }
}
