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

namespace MiGu.Server.Signal;

/// <summary>
/// 优先从 plugins/StandardScene.Signal.dll 反射表和列（Model 上的 SignalTable / DisplayName）。
/// 没有插件 DLL 时才读 signal-tables.json 或内置清单。
/// </summary>
public static class SignalTableManifestLoader
{
    private const string ManifestFileName = "signal-tables.json";

    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNameCaseInsensitive = true
    };

    public static IReadOnlyList<SignalTableDef> Load(Simple3Launcher launcher)
    {
        var workingDirectory = launcher.ResolveWorkingDirectory();
        var pluginsDir = launcher.ResolvePluginsDir();
        var fromPlugin = SignalModelSchemaResolver.ResolveTables(pluginsDir, workingDirectory);
        if (fromPlugin.Count > 0)
            return fromPlugin;

        var manifest = TryLoadManifest(launcher);
        return manifest.Tables
            .Where(t => !string.IsNullOrWhiteSpace(t.Id) && !string.IsNullOrWhiteSpace(t.FileName))
            .Select(e => ToDef(e, pluginsDir, workingDirectory))
            .ToList();
    }

    public static long ComputeManifestSignature(Simple3Launcher launcher)
    {
        long sig = 0;
        foreach (var path in ResolveManifestPaths(launcher))
        {
            if (!File.Exists(path)) continue;
            try
            {
                var info = new FileInfo(path);
                sig ^= info.LastWriteTimeUtc.Ticks;
                sig ^= info.Length;
            }
            catch { /* ignore */ }
        }

        var pluginsDir = launcher.ResolvePluginsDir();
        var dll = pluginsDir == null ? null : Path.Combine(pluginsDir, "StandardScene.Signal.dll");
        if (dll != null && File.Exists(dll))
        {
            try
            {
                var info = new FileInfo(dll);
                sig ^= info.LastWriteTimeUtc.Ticks;
            }
            catch { /* ignore */ }
        }

        return sig;
    }

    private static SignalTableManifest TryLoadManifest(Simple3Launcher launcher)
    {
        foreach (var path in ResolveManifestPaths(launcher))
        {
            if (!File.Exists(path)) continue;
            try
            {
                var json = File.ReadAllText(path);
                var manifest = JsonSerializer.Deserialize<SignalTableManifest>(json, JsonOptions);
                if (manifest?.Tables is { Count: > 0 })
                    return manifest;
            }
            catch { /* try next */ }
        }

        return JsonSerializer.Deserialize<SignalTableManifest>(EmbeddedFallbackJson, JsonOptions)
               ?? new SignalTableManifest();
    }

    private static IEnumerable<string> ResolveManifestPaths(Simple3Launcher launcher)
    {
        var wd = launcher.ResolveWorkingDirectory();
        if (!string.IsNullOrWhiteSpace(wd))
            yield return Path.Combine(wd, "Config", "Signal", ManifestFileName);

        var plugins = launcher.ResolvePluginsDir();
        if (string.IsNullOrWhiteSpace(plugins)) yield break;

        yield return Path.Combine(plugins, ManifestFileName);
        yield return Path.Combine(plugins, "Config", "Signal", ManifestFileName);
    }

    private static SignalTableDef ToDef(SignalTableManifestEntry entry, string? pluginsDir, string? workingDirectory)
    {
        var columns = BuildColumns(entry, pluginsDir, workingDirectory);
        return new SignalTableDef(
            entry.Id.Trim(),
            string.IsNullOrWhiteSpace(entry.Title) ? entry.Id.Trim() : entry.Title.Trim(),
            entry.Category ?? "",
            entry.FileName.Trim(),
            columns);
    }

    private static IReadOnlyList<SignalColumn> BuildColumns(
        SignalTableManifestEntry entry,
        string? pluginsDir,
        string? workingDirectory)
    {
        var reflected = SignalModelSchemaResolver.ResolveColumns(entry.Model, pluginsDir, workingDirectory);
        if (reflected.Count > 0)
            return reflected;

        if (entry.Columns is { Count: > 0 })
        {
            return entry.Columns
                .Where(c => !string.IsNullOrWhiteSpace(c.Key))
                .Select(c => new SignalColumn(
                    c.Key.Trim(),
                    string.IsNullOrWhiteSpace(c.Label) ? c.Key.Trim() : c.Label.Trim(),
                    string.IsNullOrWhiteSpace(c.Type) ? "string" : c.Type.Trim(),
                    c.Options,
                    string.IsNullOrWhiteSpace(c.Group) ? null : c.Group.Trim()))
                .ToList();
        }

        return Array.Empty<SignalColumn>();
    }

    private const string EmbeddedFallbackJson = """
        {
          "version": 1,
          "tables": [
            {
              "id": "stations",
              "title": "PLC机构",
              "category": "PLC数据管理",
              "fileName": "stations.json",
              "model": "PlcStationModel"
            },
            {
              "id": "docks",
              "title": "机构工位",
              "category": "PLC数据管理",
              "fileName": "station-docks.json",
              "model": "PlcStationDockModel"
            },
            {
              "id": "handshake",
              "title": "握手点",
              "category": "握手点数据管理",
              "fileName": "handshake-points.json",
              "model": "HandshakePointModel"
            },
            {
              "id": "release",
              "title": "放行点",
              "category": "放行点数据管理",
              "fileName": "release-points.json",
              "model": "ReleasePointModel"
            },
            {
              "id": "mag-control",
              "title": "磁条管控区",
              "category": "磁条交管",
              "fileName": "mag-control-areas.json",
              "model": "MagControlAreaModel"
            }
          ]
        }
        """;
}
