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

namespace MiGu.Server.Controllers;

[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
    /// <summary>
    /// 登录请求体。
    /// 会话 N+1（启动反转）：新增 <see cref="LaunchMode"/>。前端登录页让用户选 "WebOnly" / "DesktopAndWeb"；
    /// MiGu.Server 据此拉起 SimpleLite 子进程并透传 <c>--display-mode=web|web+local</c>。
    /// 历史调用方不传该字段时默认 "DesktopAndWeb"（与之前 web+local 默认行为一致，向后兼容）。
    /// </summary>
    public record LoginRequest(string Username, string Password, string Scope, string? LaunchMode = null);

    /// <summary>
    /// 登录响应。
    /// 会话 N+1 增量字段：
    /// <list type="bullet">
    ///   <item><c>RunMode</c>：根据 SimpleLite 真实拉起结果回填（WebEnabled / WebOnly / Detached）。Detached 表示后端未能拉起 SimpleLite，前端可降级展示。</item>
    ///   <item><c>LaunchStatus</c>：<see cref="SimpleLiteLauncher.LaunchResult.Status"/> 枚举字符串，前端用于精细化提示。</item>
    ///   <item><c>LaunchWarning</c>：可空告警文本；非空时前端应该弹消息条告知用户。</item>
    /// </list>
    /// </summary>
    public record LoginResponse(
        string Token,
        AuthUserDto User,
        string Scope,
        string RunMode,
        EffectivePermissions EffectivePermissions,
        string? LaunchStatus = null,
        string? LaunchWarning = null);

    public record AuthUserDto(string Id, string Username, string DisplayName, List<string> Roles);

    /// <summary>
    /// 当前会话身份的轻量摘要。
    /// 用途：前端路由守卫在受保护路由首次进入前调用 <c>GET /api/auth/me</c>，
    /// 用 [Authorize] 实校验本地 token 是否仍被服务端接受（MiGu.Server 重启后
    /// JWT secret 可能已重生 → 老 token 会被拒），同时刷新 user / scope / runMode / perm。
    /// </summary>
    public record MeResponse(
        AuthUserDto User,
        string Scope,
        string RunMode,
        EffectivePermissions EffectivePermissions);

    private const string CookieName = "simple.auth.token";

    private readonly RbacStore _rbac;
    private readonly JwtIssuer _jwt;
    private readonly SimpleLiteLauncher _launcher;
    private readonly ILogger<AuthController> _log;

    public AuthController(RbacStore rbac, JwtIssuer jwt, SimpleLiteLauncher launcher, ILogger<AuthController> log)
    {
        _rbac = rbac;
        _jwt = jwt;
        _launcher = launcher;
        _log = log;
    }

    [HttpPost("login")]
    [AllowAnonymous]
    public async Task<ActionResult<LoginResponse>> Login([FromBody] LoginRequest req)
    {
        if (string.IsNullOrWhiteSpace(req.Username))
            return BadRequest(new { message = "用户名不能为空" });
        if (req.Scope is not ("Platform" or "RCSMonitor"))
            return BadRequest(new { message = "无效 scope" });

        // 真密码校验：RbacStore.VerifyCredentials 对不存在 / 已禁用 / 密码错统一返回 null，防用户名枚举。
        var user = _rbac.VerifyCredentials(req.Username, req.Password);
        if (user == null)
            return Unauthorized(new { message = "用户名或密码错误，或账号已被停用" });

        // scope 必须落在该账号「角色覆盖的 scope」集合内（admin 角色 scope=* 覆盖全部）。
        if (!_rbac.CanUseScope(user, req.Scope))
            return StatusCode(403, new { message = $"账号 {user.Username} 没有访问 {req.Scope} 的权限" });

        // 会话 N+1：按 LaunchMode 拉起 SimpleLite 子进程（线程池执行，避免占用请求线程）。
        var launchMode = NormalizeLaunchMode(req.LaunchMode);
        SimpleLiteLauncher.LaunchResult? launchResult = null;
        try
        {
            launchResult = await Task.Run(() => _launcher.MaybeStart(launchMode));
            _log.LogInformation("SimpleLite launch result for user={User} launchMode={Mode}: Started={Started} Status={Status} Detail={Detail}",
                user.Username, launchMode, launchResult.Value.Started, launchResult.Value.Status, launchResult.Value.Detail);
        }
        catch (Exception ex)
        {
            _log.LogError(ex, "SimpleLite launch threw for user={User} launchMode={Mode}", user.Username, launchMode);
        }

        var runMode = ResolveRunMode(launchResult, launchMode);

        var (perm, roleNames, token) = BuildSession(user, req.Scope);
        SetAuthCookie(token);

        var dto = new AuthUserDto(user.Id, user.Username, user.DisplayName, roleNames);
        return Ok(new LoginResponse(token, dto, req.Scope, runMode, perm,
            LaunchStatus: launchResult?.Status,
            LaunchWarning: launchResult?.Warning));
    }

    [HttpPost("logout")]
    [AllowAnonymous]
    public IActionResult Logout()
    {
        Response.Cookies.Delete(CookieName);
        return Ok(new { ok = true });
    }

    /// <summary>用本地持有的 token / Cookie 重新拉一次当前身份。失败由 [Authorize] 自动回 401。</summary>
    [HttpGet("me")]
    [Authorize]
    public ActionResult<MeResponse> Me()
    {
        var username = User.Identity?.Name ?? User.FindFirstValue(ClaimTypes.NameIdentifier);
        var scope = User.FindFirstValue("scope");

        if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(scope))
            return Unauthorized(new { message = "身份无效" });
        if (scope is not ("Platform" or "RCSMonitor"))
            return Unauthorized(new { message = "无效 scope" });

        var user = _rbac.FindUser(username);
        if (user == null || !user.Enabled)
            return Unauthorized(new { message = "账号已失效或被停用" });

        // 账号当前是否还允许这个 scope（管理员可能在此期间调整了角色）。
        if (!_rbac.CanUseScope(user, scope))
            return StatusCode(403, new { message = $"账号 {user.Username} 没有访问 {scope} 的权限" });

        var (perm, roleNames, _) = BuildSession(user, scope);
        var dto = new AuthUserDto(user.Id, user.Username, user.DisplayName, roleNames);
        return Ok(new MeResponse(dto, scope, InferRunMode(), perm));
    }

    /// <summary>用同一身份切换 scope 并重发 token + perms。</summary>
    [HttpPost("switch-scope")]
    [Authorize]
    public ActionResult<LoginResponse> SwitchScope([FromBody] SwitchScopeRequest req)
    {
        if (req.Scope is not ("Platform" or "RCSMonitor"))
            return BadRequest(new { message = "无效 scope" });

        var username = User.Identity?.Name ?? User.FindFirstValue(ClaimTypes.NameIdentifier);
        if (string.IsNullOrEmpty(username))
            return Unauthorized(new { message = "身份无效" });

        var user = _rbac.FindUser(username);
        if (user == null || !user.Enabled)
            return Unauthorized(new { message = "账号已失效或被停用" });

        if (!_rbac.CanUseScope(user, req.Scope))
            return StatusCode(403, new { message = $"账号 {user.Username} 没有访问 {req.Scope} 的权限" });

        var (perm, roleNames, token) = BuildSession(user, req.Scope);
        SetAuthCookie(token);

        var dto = new AuthUserDto(user.Id, user.Username, user.DisplayName, roleNames);
        return Ok(new LoginResponse(token, dto, req.Scope, InferRunMode(), perm));
    }

    public record SwitchScopeRequest(string Scope);

    // ───────────────────────── 内部工具 ─────────────────────────

    /// <summary>
    /// 计算指定 scope 下的有效权限（页面 / 操作 / 控件），并颁发携带该 scope 与 ops 的 JWT。
    /// 这是登录 / me / switchScope 的公共核心，确保三条路径权限计算完全一致。
    /// </summary>
    private (EffectivePermissions perm, List<string> roleNames, string token) BuildSession(RbacUser user, string scope)
    {
        var eff = _rbac.ComputeEffective(user, scope);
        var perm = new EffectivePermissions(user.Id, 1, eff.Ops, eff.Widgets, eff.Pages);
        var roleNames = _rbac.RoleNamesOf(user);
        // ops claim 写入有效操作码（含可能的 "*"），供 RbacAdmin policy 判定管理权限。
        var token = _jwt.Issue(user.Id, user.Username, scope, roleNames, eff.Ops);
        return (perm, roleNames, token);
    }

    /// <summary>me / switchScope 不重启 SimpleLite，依据 Launcher 记录的 LastLaunchMode 反推 RunMode。</summary>
    private string InferRunMode()
    {
        var last = _launcher.LastLaunchMode;
        if (string.IsNullOrEmpty(last)) return "Detached";
        if (last == SimpleLiteLauncher.ExternalReuseLaunchMode) return "WebEnabled";
        return last.Contains("local", StringComparison.OrdinalIgnoreCase) ? "WebEnabled" : "WebOnly";
    }

    /// <summary>根据 Launcher 真实结果决定 RunMode（避免 SimpleLite 没起却假装 WebEnabled）。</summary>
    private static string ResolveRunMode(SimpleLiteLauncher.LaunchResult? result, string launchMode)
    {
        if (result is not { Started: true })
            return "Detached";
        if (result.Value.Status == "ReusingExisting")
            return "WebEnabled";
        if (!string.IsNullOrEmpty(result.Value.DisplayMode))
        {
            if (result.Value.DisplayMode.Equals("web", StringComparison.OrdinalIgnoreCase))
                return "WebOnly";
            if (result.Value.DisplayMode.Contains("local", StringComparison.OrdinalIgnoreCase))
                return "WebEnabled";
        }
        return launchMode == "WebOnly" ? "WebOnly" : "WebEnabled";
    }

    private static string NormalizeLaunchMode(string? raw)
    {
        return raw?.Trim().ToLowerInvariant() switch
        {
            "webonly" or "web-only" or "web" => "WebOnly",
            _ => "DesktopAndWeb",
        };
    }

    private void SetAuthCookie(string token)
    {
        Response.Cookies.Append(CookieName, token, new CookieOptions
        {
            HttpOnly = true,
            Secure = Request.IsHttps,
            SameSite = SameSiteMode.Lax,
            Path = "/",
            Expires = DateTimeOffset.UtcNow.AddHours(24)
        });
    }
}
