using System.Diagnostics;
using Microsoft.Extensions.Logging;

namespace MiGu.Server.Launcher;

/// <summary>
/// 将 <c>obj/Debug</c> 下最新编译的 SimpleLite 同步到 <c>bin/Debug</c>（仅当 SimpleLite 未运行时）。
/// </summary>
public static class SimpleLiteBuildSync
{
    public static bool TrySyncFromObjToBin(string contentRoot, ILogger? log = null)
    {
        if (!TryResolvePaths(contentRoot, out var objDll, out var objExe, out var binDir))
            return false;

        var binDll = Path.Combine(binDir, "SimpleLite.dll");
        var binExe = Path.Combine(binDir, "SimpleLite.exe");

        if (!File.Exists(objDll))
        {
            log?.LogDebug("[SimpleLiteBuildSync] obj DLL 不存在: {Path}", objDll);
            return false;
        }

        if (Process.GetProcessesByName("SimpleLite").Any(p => !p.HasExited))
        {
            log?.LogWarning("[SimpleLiteBuildSync] SimpleLite 仍在运行，跳过 DLL 同步。请先关闭 SimpleLite 窗口。");
            return false;
        }

        var objTime = File.GetLastWriteTimeUtc(objDll);
        if (File.Exists(binDll) && File.GetLastWriteTimeUtc(binDll) >= objTime)
        {
            log?.LogDebug("[SimpleLiteBuildSync] bin 已是最新，无需同步");
            return false;
        }

        Directory.CreateDirectory(binDir);
        File.Copy(objDll, binDll, true);
        log?.LogInformation("[SimpleLiteBuildSync] 已同步 {Src} → {Dst}", objDll, binDll);

        if (File.Exists(objExe))
        {
            File.Copy(objExe, binExe, true);
            log?.LogInformation("[SimpleLiteBuildSync] 已同步 {Src} → {Dst}", objExe, binExe);
        }

        SyncRuntimeDeps(objDll, binDir, log);

        return true;
    }

    /// <summary>
    /// 探测 SimpleLite 是否带「前往站点」路由（区分新旧 DLL）。
    /// 必须用不存在的对象 id（-1）：路由存在时后端进 handler 找不到对象，返回 JSON
    /// <c>success:false</c>（无任何副作用）；路由不存在时 EmbedIO 返回 HTML 404。
    /// 早期版本误用 car/0 + siteId=0 —— 一旦场景里真有 id=0 的车和站点，每次健康
    /// 检查都会真实派车，属严重副作用，严禁回退到真实 id。
    /// </summary>
    public static bool? ProbeGotoSiteRoute(int port = 8222)
    {
        try
        {
            using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(2) };
            using var resp = client.PostAsync(
                $"http://127.0.0.1:{port}/projection/reflection/car/-1/goto-site?siteId=-1",
                null).GetAwaiter().GetResult();
            var body = resp.Content.ReadAsStringAsync().GetAwaiter().GetResult();
            if (resp.StatusCode == System.Net.HttpStatusCode.NotFound)
                return body.Contains("<html", StringComparison.OrdinalIgnoreCase) ? false : true;
            return body.Contains("\"success\"", StringComparison.Ordinal);
        }
        catch
        {
            return null;
        }
    }

    /// <summary>同步 obj 输出目录中的运行时依赖（Costura 未嵌入或需独立存在的 DLL）。</summary>
    private static void SyncRuntimeDeps(string objDll, string binDir, ILogger? log)
    {
        var objDir = Path.GetDirectoryName(objDll);
        if (string.IsNullOrEmpty(objDir)) return;

        var names = new[] { "LessokajiWeaverUtilities.dll" };
        foreach (var name in names)
        {
            var src = Path.Combine(objDir, name);
            if (!File.Exists(src))
            {
                var deps = Path.Combine(objDir, "..", "..", "tools", "deps", name);
                deps = Path.GetFullPath(deps);
                if (File.Exists(deps)) src = deps;
                else continue;
            }

            var dst = Path.Combine(binDir, name);
            File.Copy(src, dst, true);
            log?.LogInformation("[SimpleLiteBuildSync] 已同步依赖 {Name}", name);
        }
    }

    private static bool TryResolvePaths(string contentRoot, out string objDll, out string objExe, out string binDir)
    {
        objDll = objExe = "";
        binDir = "";
        var repo = FindRepoRoot(contentRoot);
        if (repo == null) return false;
        var sl = Path.Combine(repo, "Simple", "SimpleLite");
        objDll = Path.Combine(sl, "obj", "Debug", "SimpleLite.dll");
        objExe = Path.Combine(sl, "obj", "Debug", "SimpleLite.exe");
        binDir = Path.Combine(sl, "bin", "Debug");
        return true;
    }

    private static string? FindRepoRoot(string contentRoot)
    {
        var dir = new DirectoryInfo(contentRoot);
        while (dir != null)
        {
            if (Directory.Exists(Path.Combine(dir.FullName, "Simple", "SimpleLite")))
                return dir.FullName;
            dir = dir.Parent;
        }
        return null;
    }
}
