<?php
/**
 * HuSNS - 一款免费开源的社交平台
 * 
 * @author  HYR
 * @QQ      281900864
 * @website https://huyourui.com
 * @license MIT
 * @声明    严禁用于违法违规用途
 */
class App
{
    private $controller;
    private $action;
    private $params = [];
    private $isApi = false;
    private $controllerMap = [
        'IndexController' => 'content/post/PostController.php',
        'UserController' => 'content/user/UserController.php',
        'PostController' => 'content/post/PostController.php',
        'AdminController' => 'admin/AdminController.php',
        'PluginController' => 'admin/PluginController.php',
        'ThemeController' => 'admin/ThemeController.php',
        'UpgradeController' => 'admin/UpgradeController.php',
        'NotificationController' => 'content/notification/NotificationController.php',
        'AnnouncementController' => 'content/announcement/AnnouncementController.php',
        'PointController' => 'content/point/PointController.php',
        'PointSigninController' => 'content/point/PointSigninController.php',
        'LinkController' => 'content/link/LinkController.php',
        'DownloadController' => 'content/download/DownloadController.php',
        'TopicController' => 'content/topic/TopicController.php',
        'MobileController' => 'content/mobile/MobileController.php',
        'MobileApiController' => 'content/api/MobileApiController.php',
        'StorageController' => 'plugins/storage/StorageController.php',
    ];
    
    /**
     * API控制器映射
     * @var array
     */
    private $apiControllerMap = [
        'auth' => 'ApiAuthController',
        'posts' => 'ApiPostController',
        'comments' => 'ApiCommentController',
        'users' => 'ApiUserController',
        'languages' => 'ApiLanguageController',
    ];

    public function run()
    {
        // 初始化缓存
        $this->initCache();
        
        // 全局限流检查
        $this->checkRateLimit();
        
        Hook::loadPlugins();
        Hook::trigger('app_start');

        // 检查静态文件请求
        if ($this->serveStaticFile()) {
            return;
        }

        $this->autoLogin();
        $this->checkBanned();
        $this->redirectMobile();
        $this->parseUrl();
        
        // API限流检查
        $this->checkApiRateLimit();
        
        $this->route();

        Hook::trigger('app_end');
    }
    
    /**
     * 处理静态文件请求
     * @return bool 是否处理了静态文件请求
     */
    private function serveStaticFile()
    {
        $requestUri = parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH);
        
        // 检查是否是静态文件请求
        $staticPatterns = [
            '/^\\/static\\//',
            '/^\\/templates\\/[^\\/]+\\/static\\//',
            '/^\\/upload\\//',
            '/^\\/favicon\\.ico$/',
        ];
        
        foreach ($staticPatterns as $pattern) {
            if (preg_match($pattern, $requestUri)) {
                $filePath = ROOT_PATH . ltrim($requestUri, '/');
                
                if (file_exists($filePath) && is_file($filePath)) {
                    // 获取文件扩展名
                    $ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
                    
                    // MIME 类型映射
                    $mimeTypes = [
                        'css' => 'text/css',
                        'js' => 'application/javascript',
                        'json' => 'application/json',
                        'png' => 'image/png',
                        'jpg' => 'image/jpeg',
                        'jpeg' => 'image/jpeg',
                        'gif' => 'image/gif',
                        'svg' => 'image/svg+xml',
                        'ico' => 'image/x-icon',
                        'woff' => 'font/woff',
                        'woff2' => 'font/woff2',
                        'ttf' => 'font/ttf',
                        'eot' => 'application/vnd.ms-fontobject',
                        'map' => 'application/json',
                    ];
                    
                    $contentType = $mimeTypes[$ext] ?? 'application/octet-stream';
                    
                    // 设置缓存头
                    $maxAge = 86400; // 1天
                    if (strpos($requestUri, '/static/') !== false) {
                        $maxAge = 31536000; // 1年（静态资源长期缓存）
                    }
                    
                    header('Content-Type: ' . $contentType);
                    header('Cache-Control: public, max-age=' . $maxAge);
                    header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $maxAge) . ' GMT');
                    header('X-Content-Type-Options: nosniff');
                    
                    readfile($filePath);
                    return true;
                }
            }
        }
        
        return false;
    }

    /**
     * 初始化缓存
     */
    private function initCache()
    {
        // 初始化缓存配置
        $cacheConfig = [
            'driver' => Cache::DRIVER_FILE,
            'prefix' => 'husns_',
            'expire' => 3600,
        ];
        
        // 如果配置了Redis，使用Redis缓存
        if (defined('REDIS_HOST') && class_exists('Redis')) {
            $cacheConfig['driver'] = Cache::DRIVER_REDIS;
            $cacheConfig['redis'] = [
                'host' => REDIS_HOST,
                'port' => REDIS_PORT ?? 6379,
                'password' => REDIS_PASSWORD ?? null,
                'database' => REDIS_DATABASE ?? 0,
                'timeout' => 2,
            ];
        }
        
        Cache::init($cacheConfig);
    }

    /**
     * 全局限流检查
     */
    private function checkRateLimit()
    {
        $route = isset($_GET['r']) ? $_GET['r'] : '';
        
        // 静态资源跳过限流
        $extension = pathinfo($_SERVER['REQUEST_URI'] ?? '', PATHINFO_EXTENSION);
        if (in_array(strtolower($extension), ['jpg', 'jpeg', 'png', 'gif', 'css', 'js', 'ico', 'woff', 'woff2'])) {
            return;
        }
        
        $result = RateLimiter::check('global');
        
        if (!$result['allowed']) {
            $this->rateLimitExceeded($result);
        }
        
        // 设置限流响应头
        header('X-RateLimit-Limit: ' . $result['limit']);
        header('X-RateLimit-Remaining: ' . $result['remaining']);
        header('X-RateLimit-Reset: ' . $result['reset']);
    }

    /**
     * API限流检查
     */
    private function checkApiRateLimit()
    {
        if (!$this->isApi) {
            return;
        }
        
        $route = isset($_GET['r']) ? $_GET['r'] : '';
        
        // 根据API类型应用不同的限流策略
        $type = 'api';
        if (strpos($route, 'api/auth/login') !== false) {
            $type = 'login';
        } elseif (strpos($route, 'api/auth/register') !== false) {
            $type = 'register';
        } elseif (strpos($route, 'api/auth/verifyCode') !== false) {
            $type = 'sms';
        }
        
        $result = RateLimiter::check($type);
        
        if (!$result['allowed']) {
            $this->rateLimitExceeded($result, true);
        }
        
        header('X-RateLimit-Limit: ' . $result['limit']);
        header('X-RateLimit-Remaining: ' . $result['remaining']);
        header('X-RateLimit-Reset: ' . $result['reset']);
    }

    /**
     * 限流超限处理
     */
    private function rateLimitExceeded(array $result, bool $isApi = false)
    {
        if ($isApi) {
            header('Content-Type: application/json');
            http_response_code(429);
            echo json_encode([
                'code' => 429,
                'message' => '请求过于频繁，请稍后再试',
                'retry_after' => $result['retry_after'] ?? 60,
            ]);
        } else {
            http_response_code(429);
            echo '<html><head><title>429 Too Many Requests</title></head>';
            echo '<body><h1>429 - 请求过于频繁</h1>';
            echo '<p>请 ' . ($result['retry_after'] ?? 60) . ' 秒后重试</p></body></html>';
        }
        exit;
    }

    /**
     * 移动端自动跳转
     */
    private function redirectMobile()
    {
        // POST 请求不重定向（如发布动态等操作）
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            return;
        }
        
        $route = isset($_GET['r']) ? $_GET['r'] : '';
        
        if (strpos($route, 'mobile') === 0) {
            return;
        }

        if (strpos($route, 'api/') === 0) {
            return;
        }

        if (strpos($route, 'admin') === 0) {
            return;
        }

        if (isset($_COOKIE['prefer_desktop']) && $_COOKIE['prefer_desktop'] === '1') {
            return;
        }

        if (!Helper::isMobile()) {
            return;
        }

        $mobileUrl = Helper::url('mobile');
        header('Location: ' . $mobileUrl);
        exit;
    }
    
    private function checkBanned()
    {
        if (!isset($_SESSION['user_id'])) {
            return;
        }
        
        $userModel = new UserModel();
        $banInfo = $userModel->getBanInfo($_SESSION['user_id']);
        
        if (!$banInfo || $banInfo['type'] !== 2) {
            return;
        }
        
        $route = isset($_GET['r']) ? $_GET['r'] : '';
        
        if ($route === 'user/logout') {
            return;
        }
        
        if ($route === 'user/banned') {
            return;
        }
        
        header('Location: ' . Helper::url('user/banned'));
        exit;
    }

    private function autoLogin()
    {
        if (isset($_SESSION['user_id'])) {
            return;
        }

        if (!isset($_COOKIE['remember_token'])) {
            return;
        }

        $parts = explode(':', $_COOKIE['remember_token'], 2);
        if (count($parts) !== 2) {
            return;
        }

        $userId = (int)$parts[0];
        $token = $parts[1];

        if ($userId <= 0 || empty($token)) {
            return;
        }

        $userModel = new UserModel();
        $user = $userModel->find($userId);

        if (!$user || $user['status'] != 1) {
            setcookie('remember_token', '', time() - 3600, '/');
            return;
        }

        $storedToken = $user['remember_token'];
        
        $isValid = false;
        if (hash_equals($storedToken, $token)) {
            $isValid = true;
        } elseif (hash_equals($storedToken, hash('sha256', $token))) {
            $isValid = true;
        }

        if (!$isValid) {
            setcookie('remember_token', '', time() - 3600, '/');
            return;
        }

        $_SESSION['user_id'] = $user['id'];
        $_SESSION['username'] = $user['username'];
        $_SESSION['is_admin'] = $user['is_admin'];
        
        if (!empty($storedToken) && strlen($storedToken) !== 64) {
            $newToken = bin2hex(random_bytes(32));
            $userModel->update($userId, [
                'remember_token' => hash('sha256', $newToken),
                'updated_at' => time()
            ]);
            
            $isSecure = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on';
            $expires = time() + 30 * 24 * 3600;
            
            if (PHP_VERSION_ID >= 70300) {
                setcookie('remember_token', $userId . ':' . $newToken, [
                    'expires' => $expires,
                    'path' => '/',
                    'secure' => $isSecure,
                    'httponly' => true,
                    'samesite' => 'Lax'
                ]);
            } else {
                setcookie('remember_token', $userId . ':' . $newToken, $expires, '/', '', $isSecure, true);
            }
        }
    }

    private function parseUrl()
    {
        // 优先从 GET 参数获取路由，POST 参数作为后备
        if (isset($_GET['r']) && !empty($_GET['r'])) {
            $path = trim($_GET['r'], '/');
        } elseif (isset($_POST['r']) && !empty($_POST['r'])) {
            $path = trim($_POST['r'], '/');
        } else {
            $scriptName = dirname($_SERVER['SCRIPT_NAME']);
            $scriptName = str_replace('\\', '/', $scriptName);
            $requestUri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
            
            $path = str_replace($scriptName, '', $requestUri);
            $path = trim($path, '/');
            
            if (strpos($path, 'index.php/') === 0) {
                $path = substr($path, strlen('index.php/'));
            } elseif ($path === 'index.php') {
                $path = '';
            }
        }
        
        if (empty($path)) {
            $this->controller = 'IndexController';
            $this->action = 'index';
            return;
        }
        
        if (strpos($path, 'api/') === 0) {
            $this->isApi = true;
            $this->parseApiUrl($path);
            return;
        }

        // 处理移动端API路由 mobileApi/xxx
        if (strpos($path, 'mobileApi/') === 0) {
            $this->controller = 'MobileApiController';
            $actionPath = substr($path, strlen('mobileApi/'));
            if (!empty($actionPath)) {
                $this->action = $actionPath;
            } else {
                $this->action = 'index';
            }
            return;
        }

        $segments = explode('/', $path);
        
        $controllerName = ucfirst($segments[0]) . 'Controller';
        
        if (isset($this->controllerMap[$controllerName])) {
            $this->controller = $controllerName;
            array_shift($segments);
        } else {
            $controllerFile = ROOT_PATH . 'content/' . $segments[0] . '/' . $controllerName . '.php';
            
            if (file_exists($controllerFile)) {
                $this->controller = $controllerName;
                array_shift($segments);
            } else {
                $this->controller = 'IndexController';
            }
        }

        if (!empty($segments)) {
            // 将剩余的 segments 合并成一个 action（用斜杠分隔）
            $this->action = implode('/', $segments);
            $this->params = [];
        } else {
            $this->action = 'index';
            $this->params = [];
        }
    }
    
    /**
     * 解析API路由
     * 
     * RESTful风格路由：
     * - GET    /api/posts          -> index
     * - GET    /api/posts/1        -> show
     * - POST   /api/posts          -> store
     * - PUT    /api/posts/1        -> update
     * - DELETE /api/posts/1        -> destroy
     * 
     * @param string $path URL路径
     */
    private function parseApiUrl($path)
    {
        $path = substr($path, 4);
        $path = trim($path, '/');
        
        if (empty($path)) {
            $this->notFound();
        }
        
        $segments = explode('/', $path);
        $resource = $segments[0];
        
        if (!isset($this->apiControllerMap[$resource])) {
            $this->notFound();
        }
        
        $this->controller = $this->apiControllerMap[$resource];
        array_shift($segments);
        
        $method = $_SERVER['REQUEST_METHOD'];
        $id = isset($segments[0]) && is_numeric($segments[0]) ? (int)$segments[0] : null;
        $action = isset($segments[1]) ? $segments[1] : null;
        
        if ($id !== null) {
            $_GET['id'] = $id;
            array_shift($segments);
        }
        
        if ($action !== null && !is_numeric($action)) {
            $this->action = $action;
            array_shift($segments);
        } else {
            $this->action = $this->mapMethodToAction($method, $id !== null);
        }
        
        $this->params = $segments;
    }
    
    /**
     * 映射HTTP方法到控制器动作
     * 
     * @param string $method HTTP方法
     * @param bool $hasId 是否有资源ID
     * @return string 动作名称
     */
    private function mapMethodToAction($method, $hasId)
    {
        $map = [
            'GET' => $hasId ? 'show' : 'index',
            'POST' => 'store',
            'PUT' => 'update',
            'PATCH' => 'update',
            'DELETE' => 'destroy',
        ];
        
        return $map[$method] ?? 'index';
    }

    private function route()
    {
        if ($this->isApi) {
            $file = ROOT_PATH . 'api/ApiController.php';
        } elseif (isset($this->controllerMap[$this->controller])) {
            $file = ROOT_PATH . $this->controllerMap[$this->controller];
        } else {
            $dir = strtolower(str_replace('Controller', '', $this->controller));
            $file = ROOT_PATH . 'content/' . $dir . '/' . $this->controller . '.php';
        }

        if (!file_exists($file)) {
            $this->notFound();
        }

        require_once $file;

        if (!class_exists($this->controller)) {
            $this->notFound();
        }

        $controller = new $this->controller();

        $actionExists = method_exists($controller, $this->action);
        $actionCallable = is_callable([$controller, $this->action]);
        
        // 检查是否有 __call 方法可以处理该动作
        $hasMagicCall = method_exists($controller, '__call');
        
        if (!$actionExists && !$actionCallable && !$hasMagicCall) {
            $this->notFound();
        }

        Hook::trigger('before_controller', [
            'controller' => $this->controller,
            'action' => $this->action
        ]);

        call_user_func_array([$controller, $this->action], $this->params);
    }

    private function notFound()
    {
        header('HTTP/1.0 404 Not Found');
        if (file_exists(ROOT_PATH . 'templates/default/error/404.php')) {
            $view = new View();
            echo $view->render('error/404');
        } else {
            echo '<h1>404 - 页面未找到</h1>';
        }
        exit;
    }
}
