using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using Avalonia;

namespace DsmTool;

internal static class Program
{
    [STAThread]
    public static void Main(string[] args)
    {
        // 헤드리스 CLI 모드:  DsmTool.exe --cli <대상폴더> [출력폴더]
        if (args.Length >= 2 && args[0] == "--cli")
        {
            RunCli(args);
            return;
        }

        var crash = Path.Combine(Path.GetTempPath(), "DsmTool_crash.log");
        try { File.WriteAllText(crash, "start\n"); } catch { }
        AppDomain.CurrentDomain.UnhandledException += (_, e) =>
        { try { File.AppendAllText(crash, "UNHANDLED: " + e.ExceptionObject + "\n"); } catch { } };
        try
        {
            PrepareNativeLibs();           // 임베드된 네이티브 DLL 자체 추출 (진짜 단일 exe)
            try { File.AppendAllText(crash, "natives ready\n"); } catch { }
            BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
        }
        catch (Exception ex)
        {
            try { File.AppendAllText(crash, "EX: " + ex + "\n"); } catch { }
            throw;
        }
    }

    public static AppBuilder BuildAvaloniaApp() =>
        AppBuilder.Configure<App>()
            .UsePlatformDetect()
            .LogToTrace();

    private static void RunCli(string[] args)
    {
        var pos = args.Skip(1).Where(a => a != "--cli").ToList();
        string? iniArg = null;
        var ci = pos.FindIndex(a => a == "--config");
        if (ci >= 0 && ci + 1 < pos.Count) { iniArg = pos[ci + 1]; pos.RemoveRange(ci, 2); }

        var root = pos.Count >= 1 ? pos[0].Trim().Trim('"') : ".";
        var outDir = pos.Count >= 2 ? pos[1].Trim().Trim('"') : root;
        var log = new System.Text.StringBuilder();
        void L(string m) { log.AppendLine(m); try { Console.WriteLine(m); } catch { } }

        try
        {
            var ini = iniArg ?? FindConfig(root);
            if (ini != null) L($"config: {ini}");
            var cfg = DsmConfig.Load(ini);
            var r = DsmEngine.Run(root, outDir, cfg, L);
            L($"OK nodes={r.Nodes} edges={r.Edges} upper={r.Upper}");
            L($"maxFanIn={r.MaxFanIn}({r.MaxFanInV})  maxFanOut={r.MaxFanOut}({r.MaxFanOutV})");
            L($"-> {r.HtmlPath}");
            L($"-> {r.CsvPath}");
            try { File.WriteAllText(Path.Combine(outDir, "_cli_result.txt"), log.ToString()); } catch { }
        }
        catch (Exception ex)
        {
            try { File.WriteAllText(Path.Combine(outDir, "_cli_result.txt"), log + "\nERROR: " + ex); } catch { }
        }
    }

    // dsm.config.ini 자동 탐색: 대상폴더 → exe 폴더
    internal static string? FindConfig(string root)
    {
        try
        {
            var a = Path.Combine(root, "dsm.config.ini");
            if (File.Exists(a)) return a;
            var exeDir = AppContext.BaseDirectory;
            var b = Path.Combine(exeDir, "dsm.config.ini");
            if (File.Exists(b)) return b;
        }
        catch { }
        return null;
    }

    // ---- 임베드된 네이티브 DLL 추출 후 검색경로 등록 (단일 exe 자체 실행) ----
    private static void PrepareNativeLibs()
    {
        try
        {
            var asm = Assembly.GetExecutingAssembly();
            var names = asm.GetManifestResourceNames()
                           .Where(n => n.Contains(".native.") && n.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
                           .ToArray();
            if (names.Length == 0) return; // dev 빌드(임베드 없음) → 옆의 loose dll 사용

            var dir = Path.Combine(Path.GetTempPath(), "DsmTool_native");
            Directory.CreateDirectory(dir);
            foreach (var res in names)
            {
                var file = res[(res.IndexOf(".native.", StringComparison.Ordinal) + ".native.".Length)..];
                var dest = Path.Combine(dir, file);
                using var s = asm.GetManifestResourceStream(res);
                if (s == null) continue;
                if (!File.Exists(dest) || new FileInfo(dest).Length != s.Length)
                {
                    using var fs = File.Create(dest);
                    s.CopyTo(fs);
                }
            }
            SetDllDirectory(dir);
        }
        catch { /* 실패 시 loose dll 로 폴백 */ }
    }

    [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool SetDllDirectory(string lpPathName);
}
