namespace MiGu.Server.Auth;

/// <summary>
/// 单个「权限页面」定义。Key 与前端 vue-router 的 route.name 一一对齐，
/// 角色通过勾选 Key 集合决定可访问的页面（菜单 + 路由守卫据此放行）。
/// </summary>
public sealed record PageDef(string Key, string Label, string Group, string Scope);

/// <summary>
/// 平台「权限页面清单」——RBAC 的最小授权单元。
///
/// 设计：页面是由前端路由静态决定的（相对稳定），因此后端维护一份与
/// <c>frontends/.../router/index.ts</c> 对齐的静态清单，通过 <c>GET /api/rbac/pages</c>
/// 暴露给「权限与角色」管理页，让管理员可视化地把页面分配给角色。
///
/// Scope 含义：
/// - <c>Platform</c>：管理端（/admin/*）页面；
/// - <c>RCSMonitor</c>：运营监控端（/monitor/*）页面。
/// </summary>
public static class PageCatalog
{
    public const string ScopePlatform = "Platform";
    public const string ScopeMonitor = "RCSMonitor";

    /// <summary>权限页面通配符：角色 Pages 含此值表示「该 scope 下全部页面」（超级管理员）。</summary>
    public const string Wildcard = "*";

    public static readonly IReadOnlyList<PageDef> All = new List<PageDef>
    {
        // ── 管理端 / Platform：概览 ──
        new("admin-dashboard", "总览", "概览", ScopePlatform),
        new("admin-setup", "初始配置", "概览", ScopePlatform),
        new("admin-map-monitor", "地图监控", "概览", ScopePlatform),
        new("admin-playback", "调度回放", "概览", ScopePlatform),
        new("admin-tasks", "任务管理", "概览", ScopePlatform),
        new("admin-alarms", "报警管理", "概览", ScopePlatform),
        new("admin-ops-log", "运维记录", "概览", ScopePlatform),
        new("admin-notes", "运维备注", "概览", ScopePlatform),
        new("admin-logs", "日志管理", "概览", ScopePlatform),

        // ── 管理端 / Platform：设计与编排 ──
        new("admin-maps", "地图管理", "设计与编排", ScopePlatform),
        new("admin-map-editor", "地图编辑", "设计与编排", ScopePlatform),
        new("admin-project-properties", "项目属性", "设计与编排", ScopePlatform),
        new("admin-tracks", "场景管理", "设计与编排", ScopePlatform),
        new("admin-cars", "车辆管理", "设计与编排", ScopePlatform),
        new("admin-processes", "进程管理", "设计与编排", ScopePlatform),
        new("admin-scripts", "脚本管理", "设计与编排", ScopePlatform),
        new("admin-task-templates", "任务编排", "设计与编排", ScopePlatform),
        new("admin-wcs-template-proto", "WCS模板原型", "设计与编排", ScopePlatform),
        new("admin-simple-fields", "字段管理", "设计与编排", ScopePlatform),

        // ── 管理端 / Platform：数据中心（scene.signal，选 SPS / Pack 时显示） ──
        new("admin-data-center", "数据中心", "数据中心", ScopePlatform),

        // ── 管理端 / Platform：平台配置中心（聚合页，每个 Key 对齐前端聚合路由 route.name） ──
        new("admin-vehicle-hub", "车辆运维", "平台配置中心", ScopePlatform),
        new("admin-config-facility", "设备接入", "平台配置中心", ScopePlatform),
        new("admin-config-warehouse", "库位管理", "平台配置中心", ScopePlatform),
        new("admin-config-ops-center", "监控配置", "平台配置中心", ScopePlatform),
        new("admin-config-system", "系统配置", "平台配置中心", ScopePlatform),
        new("admin-config-auth", "权限与角色", "平台配置中心", ScopePlatform),

        // ── 运营端 / RCSMonitor ──
        new("monitor-vehicle-hub", "车辆运维", "运营监控", ScopeMonitor),
        new("monitor-map", "地图监控", "运营监控", ScopeMonitor),
        new("monitor-ops", "运维记录", "运营监控", ScopeMonitor),
        new("monitor-notes", "运维备注", "运营监控", ScopeMonitor),
    };

    private static readonly HashSet<string> _keys =
        All.Select(p => p.Key).ToHashSet(StringComparer.OrdinalIgnoreCase);

    /// <summary>已下线页面 Key → 合并后的新 Key（角色数据迁移用）。</summary>
    private static readonly IReadOnlyDictionary<string, string> LegacyKeyAliases =
        new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
        {
            ["admin-config-vehicle"] = "admin-vehicle-hub",
            ["admin-config-fleet"] = "admin-vehicle-hub",
            // 会话 16：平台配置中心入口按业务收敛为 6 个聚合页，旧 Key 迁移到对应聚合页 Key。
            ["admin-config-device"] = "admin-config-facility",
            ["admin-config-location"] = "admin-config-warehouse",
            ["admin-config-ops"] = "admin-config-ops-center",
            ["admin-config-logs"] = "admin-logs",
            ["admin-config-map-monitor"] = "admin-config-ops-center",
            ["admin-config-system-center"] = "admin-config-system",
            // 运营总览页已下线：旧角色勾选迁到地图监控，避免权限清单里出现幽灵页面。
            ["monitor-dashboard"] = "monitor-map",
            ["admin-data-center-stations"] = "admin-data-center",
            ["admin-data-center-docks"] = "admin-data-center",
            ["admin-data-center-handshake"] = "admin-data-center",
            ["admin-data-center-release"] = "admin-data-center",
            ["admin-data-center-mag-control"] = "admin-data-center",
        };

    /// <summary>
    /// 管理端页面 → 运营端对应页面。给运营角色勾页时，只能从「当前管理员已有的管理端页」映射出来。
    /// 无对应运营页的管理端入口（如任务/报警）不出现在映射里。
    /// </summary>
    public static readonly IReadOnlyDictionary<string, string> PlatformToMonitor =
        new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
        {
            ["admin-map-monitor"] = "monitor-map",
            ["admin-vehicle-hub"] = "monitor-vehicle-hub",
            ["admin-ops-log"] = "monitor-ops",
            ["admin-notes"] = "monitor-notes",
        };

    /// <summary>仅运营端有的页面：不依赖管理端对应页，始终可授给运营角色。</summary>
    public static readonly IReadOnlyList<string> MonitorOnlyPages =
        new[] { "monitor-ops", "monitor-notes" };

    /// <summary>
    /// 把角色里勾选的页面展开到指定登录域：勾了运营监控的「运维记录」时，
    /// 管理账号/通用角色进管理壳也能拿到对应的 admin-ops-log。
    /// </summary>
    public static HashSet<string> ExpandKeysForScope(IEnumerable<string> keys, string scope)
    {
        var scopeKeys = KeysForScope(scope).ToHashSet(StringComparer.OrdinalIgnoreCase);
        var set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        foreach (var raw in keys ?? Array.Empty<string>())
        {
            if (string.Equals(raw, Wildcard, StringComparison.OrdinalIgnoreCase))
            {
                foreach (var k in scopeKeys) set.Add(k);
                return set;
            }
            var key = NormalizeKey(raw);
            if (scopeKeys.Contains(key)) set.Add(key);
            var counterpart = CounterpartForScope(key, scope);
            if (counterpart != null && scopeKeys.Contains(counterpart)) set.Add(counterpart);
        }
        return set;
    }

    /// <summary>管理页 ↔ 运营页互认：monitor-ops → admin-ops-log（当前域是 Platform 时）。</summary>
    public static string? CounterpartForScope(string key, string targetScope)
    {
        if (string.Equals(targetScope, ScopePlatform, StringComparison.OrdinalIgnoreCase))
        {
            foreach (var kv in PlatformToMonitor)
            {
                if (string.Equals(kv.Value, key, StringComparison.OrdinalIgnoreCase))
                    return kv.Key;
            }
            return null;
        }
        if (string.Equals(targetScope, ScopeMonitor, StringComparison.OrdinalIgnoreCase)
            && PlatformToMonitor.TryGetValue(key, out var monitorKey))
            return monitorKey;
        return null;
    }

    /// <summary>判断页面 Key 是否合法（用于角色保存时过滤掉脏数据 / 已下线页面）。</summary>
    public static bool IsValidKey(string key) => _keys.Contains(key);

    /// <summary>将旧页面 Key 映射为当前有效 Key；未知 Key 原样返回。</summary>
    public static string NormalizeKey(string key) =>
        LegacyKeyAliases.TryGetValue(key, out var mapped) ? mapped : key;

    /// <summary>列出某 scope 下的全部页面 Key（用于把角色的 "*" 通配展开成具体页面集合）。</summary>
    public static IReadOnlyList<string> KeysForScope(string scope) =>
        All.Where(p => string.Equals(p.Scope, scope, StringComparison.OrdinalIgnoreCase))
           .Select(p => p.Key)
           .ToList();

    /// <summary>当前管理员在管理端拥有的页面 → 可授给运营端角色的页面集合。</summary>
    public static HashSet<string> GrantableMonitorPages(IEnumerable<string>? actorPlatformPages)
    {
        var set = new HashSet<string>(MonitorOnlyPages, StringComparer.OrdinalIgnoreCase);
        var list = (actorPlatformPages ?? Array.Empty<string>()).ToList();
        if (list.Contains(Wildcard, StringComparer.OrdinalIgnoreCase)
            || HasAllKeys(list, KeysForScope(ScopePlatform)))
        {
            foreach (var k in KeysForScope(ScopeMonitor)) set.Add(k);
            return set;
        }
        foreach (var p in list)
        {
            var key = NormalizeKey(p);
            if (PlatformToMonitor.TryGetValue(key, out var monitorKey) && IsValidKey(monitorKey))
                set.Add(monitorKey);
        }
        return set;
    }

    /// <summary>当前管理员可授给管理端角色的页面（不能超权：只能授自己已有的）。</summary>
    public static HashSet<string> GrantablePlatformPages(IEnumerable<string>? actorPlatformPages)
    {
        var list = (actorPlatformPages ?? Array.Empty<string>()).ToList();
        var all = KeysForScope(ScopePlatform);
        if (list.Contains(Wildcard, StringComparer.OrdinalIgnoreCase) || HasAllKeys(list, all))
            return all.ToHashSet(StringComparer.OrdinalIgnoreCase);
        return list
            .Select(NormalizeKey)
            .Where(k => IsValidKey(k) && all.Contains(k, StringComparer.OrdinalIgnoreCase))
            .ToHashSet(StringComparer.OrdinalIgnoreCase);
    }

    /// <summary>按角色归属域，算出当前管理员可勾选的页面 Key。</summary>
    public static HashSet<string> GrantablePagesForRoleScope(
        string roleScope, IEnumerable<string>? actorPlatformPages)
    {
        if (string.Equals(roleScope, ScopeMonitor, StringComparison.OrdinalIgnoreCase))
            return GrantableMonitorPages(actorPlatformPages);
        if (string.Equals(roleScope, ScopePlatform, StringComparison.OrdinalIgnoreCase))
            return GrantablePlatformPages(actorPlatformPages);
        // 通用域：管理端可授 + 运营端可授
        var set = GrantablePlatformPages(actorPlatformPages);
        set.UnionWith(GrantableMonitorPages(actorPlatformPages));
        return set;
    }

    private static bool HasAllKeys(IEnumerable<string> have, IEnumerable<string> all)
    {
        var set = have.ToHashSet(StringComparer.OrdinalIgnoreCase);
        return all.All(k => set.Contains(k));
    }
}
