using System.Text.Json;
using MiGu.Server.Infra;

namespace MiGu.Server.Configs;

/// <summary>
/// 运维操作审计存储：内存队列 + <c>data/ops-audit.json</c> 原子持久化。
///
/// 取代 OpsController 旧的「纯静态 ConcurrentQueue」——那种实现进程一重启审计全丢，
/// 且无法满足「运维动作可追溯」的合规诉求。这里启动时回载历史，写入走原子落盘，
/// 仅保留最近 <see cref="MaxEntries"/> 条避免无限增长。
/// </summary>
public sealed class OpsAuditStore
{
    public sealed record AuditEntry(
        string Id,
        DateTimeOffset Ts,
        string User,
        string Scope,
        string OpCode,
        string Target,
        string Result,
        string? Message,
        string? IdempotencyKey = null);

    private const int MaxEntries = 500;

    private readonly object _gate = new();
    private readonly string _file;
    private readonly ILogger<OpsAuditStore> _logger;
    private readonly List<AuditEntry> _entries = new();
    private long _seq;

    private readonly JsonSerializerOptions _json = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        WriteIndented = false
    };

    public OpsAuditStore(IWebHostEnvironment env, ILogger<OpsAuditStore> logger)
    {
        _logger = logger;
        var dir = Path.Combine(env.ContentRootPath, "data");
        Directory.CreateDirectory(dir);
        _file = Path.Combine(dir, "ops-audit.json");
        Load();
    }

    private void Load()
    {
        try
        {
            if (!File.Exists(_file)) return;
            var list = JsonSerializer.Deserialize<List<AuditEntry>>(File.ReadAllText(_file), _json);
            if (list is { Count: > 0 })
            {
                _entries.AddRange(list.Count > MaxEntries ? list.GetRange(list.Count - MaxEntries, MaxEntries) : list);
                // 续上序号，避免重启后 id 从 A000001 重新开始造成重复。
                _seq = _entries.Select(e => ParseSeq(e.Id)).DefaultIfEmpty(0).Max();
            }
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "ops 审计文件 {File} 加载失败，将以空记录开始并备份损坏文件", _file);
            AtomicFile.BackupCorrupt(_file);
        }
    }

    private static long ParseSeq(string id) => long.TryParse(id.TrimStart('A'), out var n) ? n : 0;

    /// <summary>追加一条审计并原子落盘。返回生成的条目（含 Id）。</summary>
    public AuditEntry Append(string user, string scope, string opCode, string target, string result, string? message, string? idempotencyKey = null)
    {
        lock (_gate)
        {
            var entry = new AuditEntry(
                $"A{++_seq:D6}", DateTimeOffset.UtcNow,
                user, scope, opCode, target, result, message, idempotencyKey);
            _entries.Add(entry);
            if (_entries.Count > MaxEntries)
                _entries.RemoveRange(0, _entries.Count - MaxEntries);
            Persist();
            return entry;
        }
    }

    /// <summary>
    /// 查找指定幂等键最近一条「成功(ok)」审计，用于对重复下发去重。无则返回 null。
    /// 仅匹配成功记录：上次失败的请求允许重试重新下发。
    /// </summary>
    public AuditEntry? FindSuccessByIdempotencyKey(string? key)
    {
        if (string.IsNullOrWhiteSpace(key)) return null;
        lock (_gate)
        {
            for (var i = _entries.Count - 1; i >= 0; i--)
            {
                var e = _entries[i];
                if (e.Result == "ok" && string.Equals(e.IdempotencyKey, key, StringComparison.Ordinal))
                    return e;
            }
            return null;
        }
    }

    /// <summary>最近的审计（倒序，最新在前）。</summary>
    public IReadOnlyList<AuditEntry> Recent()
    {
        lock (_gate)
        {
            var copy = new List<AuditEntry>(_entries);
            copy.Reverse();
            return copy;
        }
    }

    private void Persist()
    {
        try
        {
            AtomicFile.WriteAllText(_file, JsonSerializer.Serialize(_entries, _json));
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "ops 审计持久化到 {File} 失败", _file);
        }
    }
}
