using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using CliFx.Exceptions;
using CliFx.Infrastructure;
using ZB.MOM.WW.GRAccess.Cli.Protocol;
using ZB.MOM.WW.GRAccess.Cli.Session;
using ArchestrA.GRAccess;
using ZB.MOM.WW.GRAccess.Cli.GRAccess;

namespace ZB.MOM.WW.GRAccess.Cli.Infrastructure
{
    public static class CommandRouter
    {
        /// <summary>
        /// Execute a command either via an active session (named pipe) or direct one-shot connection.
        /// </summary>
        /// <param name="console">CliFx console for output</param>
        /// <param name="galaxyName">Galaxy to connect to</param>
        /// <param name="nodeName">GR node (required for one-shot, ignored if session active)</param>
        /// <param name="command">Command name (e.g. "query")</param>
        /// <param name="subcommand">Subcommand name (e.g. "instances")</param>
        /// <param name="args">Command arguments</param>
        /// <param name="directExecute">Callback for one-shot execution against a live IGalaxy</param>
        /// <returns>Exit code</returns>
        public static async Task<int> ExecuteAsync(
            IConsole console,
            string galaxyName,
            string nodeName,
            string command,
            string subcommand,
            Dictionary<string, object> args,
            Func<IGalaxy, string> directExecute)
        {
            nodeName = nodeName ?? string.Empty;
            args["galaxy"] = galaxyName;
            args["node"] = nodeName;

            // 1. Try active session
            if (SessionClient.TryConnect(galaxyName, out var client))
            {
                using (client)
                {
                    var request = PipeRequest.Execute(command, subcommand, args);
                    var response = await client.SendCommandAsync(request).ConfigureAwait(false);

                    if (!response.Success)
                    {
                        if (IsLlmJson(args))
                        {
                            await console.Output.WriteLineAsync(LlmResponse.Fail(
                                DisplayCommand(command, subcommand),
                                galaxyName,
                                Target(args),
                                new InvalidOperationException(response.Error),
                                response.ExitCode)).ConfigureAwait(false);
                            return response.ExitCode;
                        }

                        throw new CommandException(response.Error, response.ExitCode);
                    }

                    if (!string.IsNullOrEmpty(response.Output))
                        await console.Output.WriteLineAsync(response.Output).ConfigureAwait(false);

                    return 0;
                }
            }

            // 2. No session — one-shot mode (already on STA thread via [STAThread])
            if (string.IsNullOrEmpty(nodeName))
            {
                if (IsLlmJson(args))
                {
                    await console.Output.WriteLineAsync(LlmResponse.Fail(
                        DisplayCommand(command, subcommand),
                        galaxyName,
                        Target(args),
                        new InvalidOperationException("No active session found. Provide --node for one-shot mode, or start a session first."),
                        1)).ConfigureAwait(false);
                    return 1;
                }

                throw new CommandException(
                    "No active session found. Provide --node for one-shot mode, or start a session first.",
                    1);
            }

            using (var connection = new GRAccessConnection(galaxyName, nodeName))
            {
                try
                {
                    connection.Connect();
                    var result = directExecute(connection.Galaxy);
                    if (!string.IsNullOrEmpty(result))
                        await console.Output.WriteLineAsync(result).ConfigureAwait(false);
                    return 0;
                }
                catch (Exception ex)
                {
                    if (IsLlmJson(args))
                    {
                        await console.Output.WriteLineAsync(LlmResponse.Fail(DisplayCommand(command, subcommand), galaxyName, Target(args), ex, 1)).ConfigureAwait(false);
                        return 1;
                    }

                    throw new CommandException($"Error: {ex.Message}", 1);
                }
            }
        }

        private static bool IsLlmJson(Dictionary<string, object> args)
        {
            return args != null
                && args.TryGetValue("llm-json", out var value)
                && value != null
                && (value is bool b ? b : bool.TryParse(Convert.ToString(value), out var parsed) && parsed);
        }

        private static string DisplayCommand(string command, string subcommand)
        {
            return $"{command} {(subcommand ?? string.Empty).Replace('-', ' ')}".Trim();
        }

        private static string Target(Dictionary<string, object> args)
        {
            if (args == null) return string.Empty;
            foreach (var key in new[] { "name", "output", "file", "path", "template", "galaxy", "pattern", "default" })
                if (args.TryGetValue(key, out var value) && value != null)
                    return Convert.ToString(value) ?? string.Empty;
            return string.Empty;
        }
    }
}
