using System.Runtime.InteropServices;
using System.Text;

namespace Zcbot.WindowsNode;

internal static class Program
{
    [STAThread]
    private static int Main(string[] args)
    {
        if (args.Length > 0)
        {
            NativeConsole.AttachToParent();
            return RunCommandAsync(args).GetAwaiter().GetResult();
        }

        ApplicationConfiguration.Initialize();
        using var singleInstance = new Mutex(
            initiallyOwned: true, "Local\\Zcbot.WindowsNode", out var isFirstInstance);
        if (!isFirstInstance)
        {
            MessageBox.Show(
                "zcbot Windows Node 已在运行。请查看系统托盘。",
                "zcbot Windows Node", MessageBoxButtons.OK, MessageBoxIcon.Information);
            return 0;
        }

        try
        {
            var store = new NodeConfigStore(NodePaths.ForCurrentMachine());
            Application.Run(new TrayApplicationContext(store));
            return 0;
        }
        catch (NodeConfigurationException exception)
        {
            MessageBox.Show(
                exception.Message,
                "zcbot Windows Node 配置无效",
                MessageBoxButtons.OK,
                MessageBoxIcon.Error);
            return 2;
        }
    }

    private static async Task<int> RunCommandAsync(string[] args)
    {
        try
        {
            var store = new NodeConfigStore(NodePaths.ForCurrentMachine());
            if (string.Equals(args[0], "enroll", StringComparison.OrdinalIgnoreCase))
            {
                var options = EnrollOptions.Parse(args[1..]);
                await EnrollmentClient.EnrollAsync(options, store, CancellationToken.None);
                Console.WriteLine("[OK] Node registration saved.");
                return 0;
            }
            if (!string.Equals(args[0], "run", StringComparison.OrdinalIgnoreCase)
                || !args.Skip(1).Contains("--headless", StringComparer.OrdinalIgnoreCase))
            {
                throw new NodeConfigurationException(
                    "Usage: Zcbot.WindowsNode enroll ... | run --headless");
            }

            using var shutdown = new CancellationTokenSource();
            Console.CancelKeyPress += (_, eventArgs) =>
            {
                eventArgs.Cancel = true;
                shutdown.Cancel();
            };
            var config = store.Load();
            await new NodeConnectionLoop(config).RunAsync(shutdown.Token);
            return 0;
        }
        catch (OperationCanceledException)
        {
            return 0;
        }
        catch (NodeConfigurationException exception)
        {
            Console.Error.WriteLine($"[ERR] {exception.Message}");
            return 2;
        }
        catch (Exception exception)
        {
            Console.Error.WriteLine($"[ERR] {exception.GetType().Name}: {exception.Message}");
            return 1;
        }
    }
}

internal static class NativeConsole
{
    private const uint AttachParentProcess = 0xFFFFFFFF;

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool AttachConsole(uint processId);

    internal static void AttachToParent()
    {
        if (!AttachConsole(AttachParentProcess))
        {
            return;
        }
        Console.SetOut(new StreamWriter(Console.OpenStandardOutput(), new UTF8Encoding(false))
        {
            AutoFlush = true,
        });
        Console.SetError(new StreamWriter(Console.OpenStandardError(), new UTF8Encoding(false))
        {
            AutoFlush = true,
        });
    }
}
