using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
using MiGu.Server.Launcher;

namespace MiGu.Server.Signal;

public sealed record SignalColumn(string Key, string Label, string Type, IReadOnlyList<string>? Options = null, string? Group = null);

public sealed record SignalTableDef(
    string Id,
    string Title,
    string Category,
    string FileName,
    IReadOnlyList<SignalColumn> Columns);

/// <summary>
/// 读写 Simple3 工作目录 <c>Config/Signal/*.json</c>，供迷毂「数据中心」表格编辑。
/// 表结构只从 Simple3 <c>plugins/StandardScene.Signal.dll</c> 反射；无插件时才读 signal-tables.json。
/// </summary>
public sealed class SignalDataStore
{
    private static readonly JsonSerializerOptions FileJson = new()
    {
        WriteIndented = true,
        Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
    };

    private static readonly object FileLock = new();

    private readonly Simple3Launcher _launcher;
    private IReadOnlyList<SignalTableDef>? _cachedTables;
    private long _cachedSignature;

    public SignalDataStore(Simple3Launcher launcher) => _launcher = launcher;

    public IReadOnlyList<SignalTableDef> GetTables()
    {
        var sig = SignalTableManifestLoader.ComputeManifestSignature(_launcher);
        if (_cachedTables == null || sig != _cachedSignature)
        {
            _cachedTables = SignalTableManifestLoader.Load(_launcher);
            _cachedSignature = sig;
        }

        return _cachedTables;
    }

    public SignalTableDef? Find(string id) =>
        GetTables().FirstOrDefault(t => string.Equals(t.Id, id, StringComparison.OrdinalIgnoreCase));

    public string? ResolveWorkingDirectory() => _launcher.ResolveWorkingDirectory();

    public (string? Path, string? Error) ResolveFile(SignalTableDef table, bool createDir)
    {
        var wd = _launcher.ResolveWorkingDirectory();
        if (string.IsNullOrWhiteSpace(wd))
            return (null, "未找到 Simple3 工作目录，无法定位 Config/Signal");

        if (!TryResolveUnderSignalDir(wd, table.FileName, out var dest, out var pathError))
            return (null, pathError);

        if (File.Exists(dest))
            return (dest, null);

        if (createDir)
        {
            try
            {
                var dir = Path.GetDirectoryName(dest);
                if (!string.IsNullOrEmpty(dir))
                    Directory.CreateDirectory(dir);
            }
            catch (Exception ex)
            {
                return (null, ex.Message);
            }
        }

        return (dest, null);
    }

    public JsonArray LoadRows(SignalTableDef table)
    {
        var (path, _) = ResolveFile(table, createDir: false);
        if (path == null || !File.Exists(path))
            return new JsonArray();

        lock (FileLock)
        {
            var json = File.ReadAllText(path);
            if (string.IsNullOrWhiteSpace(json))
                return new JsonArray();
            var node = JsonNode.Parse(json);
            if (node is JsonArray arr)
                return arr;
            return new JsonArray();
        }
    }

    public void SaveRows(SignalTableDef table, JsonArray rows)
    {
        var (path, error) = ResolveFile(table, createDir: true);
        if (path == null)
            throw new InvalidOperationException(error ?? "无法解析信号配置文件路径");

        var dir = Path.GetDirectoryName(path);
        if (!string.IsNullOrEmpty(dir))
            Directory.CreateDirectory(dir);

        lock (FileLock)
            File.WriteAllText(path, rows.ToJsonString(FileJson));
    }

    private static bool TryResolveUnderSignalDir(string workingDirectory, string fileName, out string dest, out string error)
    {
        dest = "";
        error = "";
        if (string.IsNullOrWhiteSpace(fileName) ||
            Path.IsPathRooted(fileName) ||
            fileName.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
        {
            error = "信号表文件名非法";
            return false;
        }

        var root = Path.GetFullPath(Path.Combine(workingDirectory, "Config", "Signal"));
        dest = Path.GetFullPath(Path.Combine(root, fileName));
        var prefix = root.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar;
        if (!dest.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
        {
            error = "信号表路径超出 Config/Signal";
            dest = "";
            return false;
        }

        return true;
    }
}
