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

namespace MiGu.Server.Configs;

/// <summary>
/// 配置中心存储（内存 + JSON 文件持久化占位）。
/// 前 14 个 section 对应 ARCHITECTURE.md §9 的 13+1 维度；外加 deployment —— 登录后「配置向导」的部署画像。
/// 真实落地时由 SimpleShared.Persistence 接入 EF Core，并配合 YARP 下发至 SimpleLite。
/// </summary>
public sealed class ConfigStore
{
    public static readonly string[] AllSections =
    {
        "system", "integrations", "routing", "vehicle", "charge", "task",
        "traffic", "auth", "device", "fleet", "scenario", "location", "ops", "widget",
        "deployment"
    };

    public sealed record Envelope(string Section, int Version, DateTimeOffset UpdatedAt, object Payload);

    private readonly ConcurrentDictionary<string, Envelope> _mem = new(StringComparer.OrdinalIgnoreCase);
    private readonly string _dataDir;
    private readonly ILogger<ConfigStore> _logger;
    private readonly JsonSerializerOptions _jsonOpts = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        WriteIndented = true
    };

    public ConfigStore(IWebHostEnvironment env, ILogger<ConfigStore> logger)
    {
        _logger = logger;
        _dataDir = Path.Combine(env.ContentRootPath, "data");
        Directory.CreateDirectory(_dataDir);
        SeedAndLoad();
    }

    public Envelope Get(string section)
    {
        section = section.ToLowerInvariant();
        if (_mem.TryGetValue(section, out var env))
        {
            if (section == "ops") return MergeOpsEnvelope(env);
            return env;
        }
        var def = NewDefault(section);
        _mem[section] = def;
        Persist(def);
        return def;
    }

    public Envelope Put(string section, JsonElement payload)
    {
        section = section.ToLowerInvariant();
        if (!AllSections.Contains(section))
            throw new ArgumentException($"未知 section: {section}");

        var prev = _mem.TryGetValue(section, out var p) ? p : null;
        var version = (prev?.Version ?? 0) + 1;
        // payload 保留 JsonElement 原样；序列化器会按 camelCase 输出
        var env = new Envelope(section, version, DateTimeOffset.UtcNow, JsonElementToObject(payload));
        if (section == "ops") env = MergeOpsEnvelope(env);
        _mem[section] = env;
        Persist(env);
        return env;
    }

    public IEnumerable<Envelope> List() => AllSections.Select(Get);

    /// <summary>
    /// 强类型读取部署画像（<c>deployment</c> section）。兼容 Payload 为 <see cref="DeploymentProfile"/>（默认值场景）
    /// 或 <see cref="JsonElement"/>（已持久化场景）两种形态，并把 null 列表/空字符串规整为安全默认值，
    /// 供 AuthController（NeedsWizard）/ WizardController / Launcher 直接使用。
    /// </summary>
    public DeploymentProfile GetDeployment()
    {
        var env = Get("deployment");
        var dp = env.Payload switch
        {
            DeploymentProfile d => d,
            JsonElement el => SafeDeserializeDeployment(el),
            _ => DeploymentProfile.Default()
        };
        return new DeploymentProfile(
            dp.Configured,
            string.IsNullOrWhiteSpace(dp.PlatformType) ? "standard" : dp.PlatformType,
            dp.Modules ?? new List<string>(),
            dp.NavigationKinds ?? new List<string>(),
            dp.Scenarios ?? new List<string>(),
            dp.UpdatedBy ?? "");
    }

    /// <summary>以强类型保存部署画像（统一经 <see cref="Put"/> 走版本/持久化/camelCase 序列化）。</summary>
    public Envelope PutDeployment(DeploymentProfile profile)
    {
        var el = JsonSerializer.SerializeToElement(profile, _jsonOpts);
        return Put("deployment", el);
    }

    private DeploymentProfile SafeDeserializeDeployment(JsonElement el)
    {
        try { return el.Deserialize<DeploymentProfile>(_jsonOpts) ?? DeploymentProfile.Default(); }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "反序列化 deployment 失败，回退默认值");
            return DeploymentProfile.Default();
        }
    }

    private static object JsonElementToObject(JsonElement el)
    {
        // 简化：直接保留 JsonElement，让 STJ 在响应时再原样写回
        return el.Clone();
    }

    private void SeedAndLoad()
    {
        foreach (var s in AllSections)
        {
            var file = FilePath(s);
            if (File.Exists(file))
            {
                try
                {
                    var json = File.ReadAllText(file);
                    var doc = JsonDocument.Parse(json);
                    var root = doc.RootElement;
                    var version = root.TryGetProperty("version", out var v) && v.ValueKind == JsonValueKind.Number
                        ? v.GetInt32() : 1;
                    var updated = root.TryGetProperty("updatedAt", out var u) && u.ValueKind == JsonValueKind.String
                        ? DateTimeOffset.Parse(u.GetString()!) : DateTimeOffset.UtcNow;
                    var payload = root.TryGetProperty("payload", out var pl)
                        ? (object)pl.Clone()
                        : DefaultPayload(s);
                    _mem[s] = new Envelope(s, version, updated, payload);
                    continue;
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "加载 {Section} 失败，回退到默认值", s);
                    // S2：疑似损坏的配置先备份，避免随后写入的默认值把用户配置永久冲掉。
                    var bak = AtomicFile.BackupCorrupt(file);
                    if (bak != null) _logger.LogWarning("已备份疑似损坏的 {Section} 配置到 {Backup}", s, bak);
                }
            }
            _mem[s] = NewDefault(s);
            Persist(_mem[s]);
        }
    }

    private Envelope NewDefault(string section)
    {
        return new Envelope(section, 1, DateTimeOffset.UtcNow, DefaultPayload(section));
    }

    private static object DefaultPayload(string section) => section switch
    {
        "system" => SystemConfig.Default(),
        "integrations" => ExternalIntegrations.Default(),
        "routing" => RoutingPolicy.Default(),
        "vehicle" => VehicleMaintenancePolicy.Default(),
        "charge" => ChargePolicy.Default(),
        "task" => TaskAllocationPolicy.Default(),
        "traffic" => TrafficRule.Default(),
        "auth" => AuthRoleConfig.Default(),
        "device" => DeviceManagementConfig.Default(),
        "fleet" => FleetLifecycleConfig.Default(),
        "scenario" => ScenarioTemplateConfig.Default(),
        "location" => LocationManagement.Default(),
        "ops" => OpsConfig.Default(),
        "widget" => CustomWidgetConfig.Default(),
        "deployment" => DeploymentProfile.Default(),
        _ => new { }
    };

    private void Persist(Envelope env)
    {
        try
        {
            var toWrite = env.Section == "ops" ? MergeOpsEnvelope(env) : env;
            var root = new Dictionary<string, object?>
            {
                ["section"] = toWrite.Section,
                ["version"] = toWrite.Version,
                ["updatedAt"] = toWrite.UpdatedAt,
                ["payload"] = toWrite.Payload
            };
            var json = JsonSerializer.Serialize(root, _jsonOpts);
            AtomicFile.WriteAllText(FilePath(env.Section), json);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "持久化 {Section} 失败", env.Section);
        }
    }

    private static Envelope MergeOpsEnvelope(Envelope env)
    {
        if (env.Payload is not JsonElement el || el.ValueKind != JsonValueKind.Object)
            return env;

        var def = OpsConfig.Default();
        var merged = JsonSerializer.SerializeToElement(new
        {
            playback = ReadSection(el, "playback") ?? def.Playback,
            logRetention = ReadSection(el, "logRetention") ?? def.LogRetention,
            version = ReadSection(el, "version") ?? def.Version,
            monitor = MergeOpsMonitor(el, def.Monitor)
        }, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });

        return env with { Payload = merged.Clone() };
    }

    private static object? ReadSection(JsonElement root, string name) =>
        root.TryGetProperty(name, out var p) ? p.Clone() : null;

    private static JsonElement MergeOpsMonitor(JsonElement root, MonitorOpsPolicy def)
    {
        var opts = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
        if (!root.TryGetProperty("monitor", out var m) || m.ValueKind != JsonValueKind.Object)
            return JsonSerializer.SerializeToElement(def, opts);

        var car = m.TryGetProperty("car", out var c) ? c.Clone() : JsonSerializer.SerializeToElement(def.Car, opts);
        var site = m.TryGetProperty("site", out var s) ? s.Clone() : JsonSerializer.SerializeToElement(def.Site, opts);
        var track = m.TryGetProperty("track", out var t) ? t.Clone() : JsonSerializer.SerializeToElement(def.Track, opts);
        var carActionByType = m.TryGetProperty("carActionByType", out var cat)
            ? cat.Clone()
            : JsonSerializer.SerializeToElement(def.CarActionByType, opts);

        return JsonSerializer.SerializeToElement(new { car, site, track, carActionByType }, opts);
    }

    private string FilePath(string section) => Path.Combine(_dataDir, $"config-{section}.json");
}
