using System.Collections.Concurrent;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace MiGu.Server.Controllers;

/// <summary>
/// 运维白名单网关（占位）。真实落地时按架构 §5.1 + §10.5：
/// - 校验 scope=RCSMonitor 是否允许该 op；
/// - YARP 转发到 SimpleLite /api/ops/*；
/// - 写 OpsAuditLog（simple_main.db） + 写 OpsLogs（platform.db）。
///
/// AR-4: 全 class 加 [Authorize] —— 至少要求登录，再按 op 白名单 + JWT ops claim 双校验。
/// </summary>
[ApiController]
[Authorize]
[Route("api/sl/ops")]
public class OpsController : ControllerBase
{
    public record ExecuteRequest(string OpCode, string TargetId, string? Reason, string? IdempotencyKey);
    public record ExecuteResponse(bool Ok, string AuditId, string? Message);
    public record AuditEntry(string Id, DateTimeOffset Ts, string User, string Scope, string OpCode, string Target, string Result, string? Message);

    private static readonly ConcurrentQueue<AuditEntry> Audits = new();
    private static long _seq = 0;

    private static readonly HashSet<string> Whitelist = new(StringComparer.Ordinal)
    {
        "ops.car.pause", "ops.car.resume", "ops.car.gohome", "ops.car.resetSession",
        "ops.car.manualCharge", "ops.task.pause", "ops.task.cancel", "ops.task.reassign",
        "ops.task.boostPriority", "monitor.note.write"
    };

    [HttpPost("execute")]
    public ActionResult<ExecuteResponse> Execute([FromBody] ExecuteRequest req)
    {
        if (!Whitelist.Contains(req.OpCode))
            return BadRequest(new { message = $"非白名单 op：{req.OpCode}" });

        // AR-4: JWT ops claim 二次校验 —— JWT 颁发时已写入用户被授权的 ops 列表（空格分隔），
        // 这里要求 (op 在白名单) AND (op 在用户 ops claim)；admin 的 "*" 会被特判通过。
        var opsClaim = User.FindFirst("ops")?.Value ?? "";
        var userOps = opsClaim.Split(' ', StringSplitOptions.RemoveEmptyEntries);
        if (!userOps.Contains("*") && !userOps.Contains(req.OpCode))
            return StatusCode(403, new { message = $"当前账号无权执行 {req.OpCode}" });

        var user = User.Identity?.Name ?? User.FindFirstValue(ClaimTypes.NameIdentifier) ?? "anonymous";
        var scope = User.FindFirst("scope")?.Value ?? "unknown";

        var id = $"A{Interlocked.Increment(ref _seq):D6}";
        var entry = new AuditEntry(id, DateTimeOffset.UtcNow, user, scope,
            req.OpCode, req.TargetId, "ok", req.Reason);
        Audits.Enqueue(entry);
        while (Audits.Count > 200 && Audits.TryDequeue(out _)) { }
        return Ok(new ExecuteResponse(true, id, null));
    }

    [HttpGet("audits")]
    public IActionResult Audits200()
    {
        return Ok(Audits.Reverse());
    }
}
