<?php

namespace App\Http\Middleware;

use App\Http\Responses\ApiResponse;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class CheckPermission
{
    /**
     * 资源名复数→单数映射
     */
    private const SINGULAR_MAP = [
        'users' => 'user',
        'roles' => 'role',
        'menus' => 'menu',
        'logs' => 'log',
        'attachments' => 'attachment',
        'attachment-groups' => 'attachment-group',
        'config-groups' => 'config-group',
        'config-items' => 'config-item',
        'front-users' => 'front-user',
        'user-groups' => 'user-group',
        'operation-logs' => 'operation-log',
        'content-categories' => 'content-category',
        'content-models' => 'content-model',
        'content-model-fields' => 'content-model-field',
        'contents' => 'content',
        'database' => 'database',
        'admin-user-ext-fields' => 'admin-user-ext-field',
        'user-ext-fields' => 'user-ext-field',
        'app-logs' => 'app-log',
    ];

    /**
     * 白名单精确路径（不需要权限校验）
     */
    private const WHITELIST_EXACT = [
        'config.json',
        'menus.json',
        'dashboard/stats',
        'server-info',
        'cache/clear',
    ];

    /**
     * 白名单路径前缀（该前缀下的所有路径均不需要权限校验）
     */
    private const WHITELIST_PREFIXES = [
        'auth/',
        'apps/',
        'market/',
    ];

    /**
     * 嵌套资源子路径（PUT/PATCH 时映射为 .subpath.update）
     */
    private const NESTED_RESOURCE_SUBPATHS = ['permissions', 'menus'];

    public function handle(Request $request, Closure $next)
    {
        $user = $request->user('admin');

        if (! $user) {
            return response()->json(ApiResponse::error(40003, '登录已过期，请重新登录'), 401);
        }

        if ($user->isSuperAdmin()) {
            return $next($request);
        }

        $permissionCode = $this->derivePermissionCode($request);

        if ($permissionCode === null) {
            return $next($request);
        }

        // 权限码不存在于 admin_permissions 表中则直接放行
        if (! $this->permissionExists($permissionCode)) {
            return $next($request);
        }

        $permissions = cache()->remember(
            "user:{$user->id}:permissions",
            config('cmspro.cache.permission_ttl', 3600),
            fn () => $user->getAllPermissions()
        );

        if (! in_array($permissionCode, $permissions)) {
            return response()->json(ApiResponse::error(40101, '无访问权限'), 403);
        }

        return $next($request);
    }

    /**
     * 从路由 URI 模板和 HTTP 方法推导权限码
     */
    private function derivePermissionCode(Request $request): ?string
    {
        $route = $request->route();
        if (! $route) {
            return null;
        }

        $uri = $route->uri();
        $prefix = 'api/admin/';

        if (! str_starts_with($uri, $prefix)) {
            return null;
        }

        // 用实际请求路径检查白名单
        $path = $request->path();
        $relativePath = str_starts_with($path, $prefix)
            ? substr($path, strlen($prefix))
            : $path;

        if ($this->isWhitelisted($relativePath)) {
            return null;
        }

        // 解析 URI 模板，提取路径段
        $relativeUri = substr($uri, strlen($prefix));
        $segments = array_values(array_filter(explode('/', $relativeUri)));

        if (empty($segments)) {
            return null;
        }

        // 第一段：资源名（复数→单数）
        $resource = array_shift($segments);
        $module = self::SINGULAR_MAP[$resource] ?? $resource;

        // 分离命名段与参数段
        $namedSegments = [];
        $hasParameters = false;

        foreach ($segments as $segment) {
            if (preg_match('/^\{.+\}$/', $segment)) {
                $hasParameters = true;
            } else {
                $namedSegments[] = $segment;
            }
        }

        $method = $request->method();

        // 无命名段：资源根路径或纯 {id} 路径
        if (empty($namedSegments)) {
            if ($hasParameters) {
                // {id} 路径：GET→show, PUT→update, DELETE→destroy
                $action = match ($method) {
                    'GET', 'HEAD' => 'show',
                    'PUT', 'PATCH' => 'update',
                    'DELETE' => 'destroy',
                    default => null,
                };
            } else {
                // 资源根路径：GET→index, POST→store
                $action = match ($method) {
                    'GET', 'HEAD' => 'index',
                    'POST' => 'store',
                    default => null,
                };
            }

            return $action ? "admin.{$module}.{$action}" : null;
        }

        // 有命名段：构建动作码
        // 嵌套资源子路径在 PUT/PATCH 时追加 .update
        $lastIndex = count($namedSegments) - 1;
        $lastNamed = $namedSegments[$lastIndex];

        if (in_array($lastNamed, self::NESTED_RESOURCE_SUBPATHS)
            && in_array($method, ['PUT', 'PATCH'])
        ) {
            $namedSegments[$lastIndex] = $lastNamed . '.update';
        }

        $action = implode('.', $namedSegments);

        return "admin.{$module}.{$action}";
    }

    /**
     * 检查路径是否在白名单中
     */
    private function isWhitelisted(string $relativePath): bool
    {
        if (in_array($relativePath, self::WHITELIST_EXACT)) {
            return true;
        }

        foreach (self::WHITELIST_PREFIXES as $prefix) {
            if (str_starts_with($relativePath, $prefix)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 检查权限码是否存在于 admin_permissions 表
     */
    private function permissionExists(string $code): bool
    {
        return cache()->remember(
            "permission:exists:{$code}",
            config('cmspro.cache.permission_ttl', 3600),
            fn () => DB::table('admin_permissions')->where('code', $code)->exists()
        );
    }
}
