using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MiGu.Server.Auth;
using MiGu.Server.Configs;

namespace MiGu.Server.Controllers;

/// <summary>
/// RBAC 管理端：用户 / 角色 / 权限页面分配。整个控制器要求 <c>RbacAdmin</c> 策略
/// （JWT 的 ops claim 含 <c>*</c> 或 <c>auth.manage</c>），即只有「超级管理员」类账号可访问。
///
/// 对应前端「平台配置中心 → 权限与角色」页（/admin/config/auth）。
/// </summary>
[ApiController]
[Authorize(Policy = "RbacAdmin")]
[Route("api/rbac")]
public class RbacController : ControllerBase
{
    public sealed record OpDef(string Code, string Label);
    public sealed record WidgetDef(string Id, string Label);

    /// <summary>可分配的操作码候选（管理端配置角色时下拉/勾选用）。</summary>
    private static readonly OpDef[] KnownOps =
    {
        new("*", "全部操作（通配）"),
        new("ops.car.pause", "车辆 · 暂停"),
        new("ops.car.resume", "车辆 · 恢复"),
        new("ops.car.gohome", "车辆 · 回库"),
        new("ops.car.resetSession", "车辆 · 重置会话"),
        new("ops.car.manualCharge", "车辆 · 手动充电"),
        new("ops.car.execute", "车辆 · 地图监控动作（按管理端配置）"),
        new("ops.task.pause", "任务 · 暂停"),
        new("ops.task.cancel", "任务 · 取消"),
        new("ops.task.reassign", "任务 · 改派"),
        new("ops.task.boostPriority", "任务 · 提升优先级"),
        new("ops.ota", "OTA · 运维读写"),
        new("ops.ota.write", "OTA · 写操作"),
        new("monitor.note.write", "监控 · 写运营备注"),
        new("auth.manage", "系统 · 权限与角色管理"),
    };

    /// <summary>可配置可见性的控件候选。</summary>
    private static readonly WidgetDef[] KnownWidgets =
    {
        new("MapEditor", "地图编辑器"),
        new("CadToolbar", "CAD 工具栏"),
        new("CarPanel", "车辆面板"),
        new("MissionEditor", "任务编辑器"),
        new("OpsActionPanel", "运维操作面板"),
        new("ConfigCenter", "配置中心"),
    };

    private readonly RbacStore _store;
    private readonly ILogger<RbacController> _log;

    public RbacController(RbacStore store, ILogger<RbacController> log)
    {
        _store = store;
        _log = log;
    }

    /// <summary>权限「字典」：页面清单 + 可选操作码 + 可选控件 + scope 选项。前端角色编辑器据此渲染勾选项。</summary>
    [HttpGet("catalog")]
    public IActionResult Catalog()
    {
        var actorPages = ActorPlatformPages();
        var grantablePlatform = PageCatalog.GrantablePlatformPages(actorPages).OrderBy(x => x).ToList();
        var grantableMonitor = PageCatalog.GrantableMonitorPages(actorPages).OrderBy(x => x).ToList();
        return Ok(new
        {
            pages = PageCatalog.All,
            ops = KnownOps,
            widgets = KnownWidgets,
            scopes = new[]
            {
                new { value = PageCatalog.ScopePlatform, label = "管理权限 (Platform)" },
                new { value = PageCatalog.ScopeMonitor, label = "运营权限 (RCSMonitor)" },
                new { value = PageCatalog.Wildcard, label = "通用 (全部域)" },
            },
            // 当前登录管理员可勾选的页面（运营端由管理端已有页映射而来）。
            grantablePages = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase)
            {
                [PageCatalog.ScopePlatform] = grantablePlatform,
                [PageCatalog.ScopeMonitor] = grantableMonitor,
                [PageCatalog.Wildcard] = grantablePlatform.Concat(grantableMonitor).Distinct(StringComparer.OrdinalIgnoreCase).OrderBy(x => x).ToList(),
            },
            platformToMonitor = PageCatalog.PlatformToMonitor
                .Select(kv => new { platform = kv.Key, monitor = kv.Value })
                .ToList(),
            monitorOnlyPages = PageCatalog.MonitorOnlyPages,
        });
    }

    // ───────────────────────── 角色 ─────────────────────────

    [HttpGet("roles")]
    public IActionResult ListRoles() => Ok(_store.ListRoles());

    [HttpPost("roles")]
    public IActionResult CreateRole([FromBody] SaveRoleRequest req) => Guard(() =>
        Ok(_store.CreateRole(ClampPages(req))));

    [HttpPut("roles/{id}")]
    public IActionResult UpdateRole(string id, [FromBody] SaveRoleRequest req) => Guard(() =>
        Ok(_store.UpdateRole(id, ClampPages(req))));

    [HttpDelete("roles/{id}")]
    public IActionResult DeleteRole(string id) => Guard(() =>
    {
        _store.DeleteRole(id);
        return Ok(new { ok = true });
    });

    // ───────────────────────── 用户 ─────────────────────────

    [HttpGet("users")]
    public IActionResult ListUsers() => Ok(_store.ListUsers());

    [HttpPost("users")]
    public IActionResult CreateUser([FromBody] CreateUserRequest req) => Guard(() => Ok(_store.CreateUser(req)));

    [HttpPut("users/{id}")]
    public IActionResult UpdateUser(string id, [FromBody] UpdateUserRequest req) => Guard(() =>
    {
        // 自我保护：禁止把当前登录账号自己停用，避免管理员把自己锁在门外。
        if (id == CurrentUserId() && req.Enabled == false)
            return (IActionResult)BadRequest(new { message = "不能停用当前登录的账号" });
        return Ok(_store.UpdateUser(id, req));
    });

    [HttpPut("users/{id}/password")]
    public IActionResult SetPassword(string id, [FromBody] SetPasswordRequest req) => Guard(() =>
    {
        _store.SetPassword(id, req.Password);
        return Ok(new { ok = true });
    });

    [HttpDelete("users/{id}")]
    public IActionResult DeleteUser(string id) => Guard(() =>
    {
        if (id == CurrentUserId())
            return (IActionResult)BadRequest(new { message = "不能删除当前登录的账号" });
        _store.DeleteUser(id);
        return Ok(new { ok = true });
    });

    // ───────────────────────── 工具 ─────────────────────────

    /// <summary>统一把 <see cref="RbacException"/> 翻译成 400 + message，其余异常向上抛。</summary>
    private IActionResult Guard(Func<IActionResult> action)
    {
        try { return action(); }
        catch (RbacException ex) { return BadRequest(new { message = ex.Message }); }
    }

    private string? CurrentUserId() =>
        User.FindFirstValue("sub") ?? User.FindFirstValue(ClaimTypes.NameIdentifier);

    /// <summary>当前登录管理员在管理端的有效页面集合。</summary>
    private List<string> ActorPlatformPages()
    {
        var id = CurrentUserId();
        if (string.IsNullOrEmpty(id)) return new();
        var user = _store.FindUserById(id);
        if (user is null) return new();
        return _store.ComputeEffective(user, PageCatalog.ScopePlatform).Pages;
    }

    /// <summary>
    /// 保存角色时按「当前管理员可授页面」裁剪：运营端页只能选自管理端已有页的映射 + 运营专属页。
    /// 通配 <c>*</c> 仅当可授集合已覆盖该域全部页面时才保留。
    /// </summary>
    private SaveRoleRequest ClampPages(SaveRoleRequest req)
    {
        var scope = string.IsNullOrWhiteSpace(req.Scope) ? PageCatalog.ScopePlatform : req.Scope.Trim();
        var grantable = PageCatalog.GrantablePagesForRoleScope(scope, ActorPlatformPages());
        var pages = req.Pages ?? new List<string>();
        if (pages.Contains(PageCatalog.Wildcard, StringComparer.OrdinalIgnoreCase))
        {
            var scopeKeys = scope == PageCatalog.Wildcard
                ? PageCatalog.All.Select(p => p.Key).ToList()
                : PageCatalog.KeysForScope(scope).ToList();
            if (scopeKeys.All(k => grantable.Contains(k)))
                return req with { Pages = new List<string> { PageCatalog.Wildcard } };
            return req with
            {
                Pages = scopeKeys.Where(k => grantable.Contains(k)).Distinct(StringComparer.OrdinalIgnoreCase).ToList()
            };
        }
        var clamped = pages
            .Select(PageCatalog.NormalizeKey)
            .Where(p => p == PageCatalog.Wildcard || (PageCatalog.IsValidKey(p) && grantable.Contains(p)))
            .Distinct(StringComparer.OrdinalIgnoreCase)
            .ToList();
        return req with { Pages = clamped };
    }
}
