using System;
using System.Collections.Generic;
using System.Linq;
using ArchestrA.GRAccess;
using Newtonsoft.Json;

namespace ZB.MOM.WW.GRAccess.Cli.GRAccess
{
    internal static class GRAccessQueryCommandHandler
    {
        public static string Execute(
            IGalaxy galaxy,
            string command,
            string subcommand,
            IDictionary<string, string> args)
        {
            command = (command ?? string.Empty).Trim().ToLowerInvariant();
            subcommand = (subcommand ?? string.Empty).Trim().ToLowerInvariant();
            args = args ?? new Dictionary<string, string>();

            if (command == "object" && subcommand == "list")
                return ExecuteObjectList(galaxy, args);

            if (command == "template" && subcommand == "list")
                return ExecuteTypedList(galaxy, args, GRAccessObjectKind.Template);

            if (command == "instance" && subcommand == "list")
                return ExecuteTypedList(galaxy, args, GRAccessObjectKind.Instance);

            if (command == "object" && subcommand == "attributes")
                return ExecuteObjectAttributes(galaxy, args);

            throw new NotSupportedException($"Command '{command} {subcommand}' is not implemented.");
        }

        public static string ExecuteObjectList(IGalaxy galaxy, IDictionary<string, string> args)
        {
            var kind = ParseKind(GetArg(args, "type", "all"));
            var pattern = GetArg(args, "pattern", "%");
            var json = GetBoolArg(args, "json");
            var objects = GRAccessQueryService.QueryObjects(galaxy, kind, pattern)
                .OrderBy(o => o.Kind)
                .ThenBy(o => o.Tagname)
                .ToList();

            if (json)
                return JsonConvert.SerializeObject(objects, Formatting.Indented);

            return string.Join(
                Environment.NewLine,
                objects.Select(o => $"{o.Kind}\t{o.Tagname}\t{o.HierarchicalName}"));
        }

        public static string ExecuteTypedList(
            IGalaxy galaxy,
            IDictionary<string, string> args,
            GRAccessObjectKind kind)
        {
            var pattern = GetArg(args, "pattern", "%");
            var json = GetBoolArg(args, "json");
            var objects = GRAccessQueryService.QueryObjects(galaxy, kind, pattern)
                .OrderBy(o => o.Tagname)
                .ToList();

            if (json)
                return JsonConvert.SerializeObject(objects, Formatting.Indented);

            return string.Join(Environment.NewLine, objects.Select(o => o.Tagname));
        }

        public static string ExecuteObjectAttributes(IGalaxy galaxy, IDictionary<string, string> args)
        {
            var kind = ParseKind(GetArg(args, "type", "all"));
            var objectName = GetArg(args, "name", string.Empty);
            var configurableOnly = GetBoolArg(args, "configurable");
            var json = GetBoolArg(args, "json");
            var attributes = GRAccessQueryService.QueryAttributes(
                    galaxy,
                    kind,
                    objectName,
                    configurableOnly)
                .ToList();

            if (json)
                return JsonConvert.SerializeObject(attributes, Formatting.Indented);

            return string.Join(
                Environment.NewLine,
                attributes.Select(a => $"{a.Name}\t{a.DataType}\t{a.Category}"));
        }

        public static GRAccessObjectKind ParseKind(string value)
        {
            switch ((value ?? string.Empty).Trim().ToLowerInvariant())
            {
                case "":
                case "all":
                    return GRAccessObjectKind.All;
                case "template":
                case "templates":
                    return GRAccessObjectKind.Template;
                case "instance":
                case "instances":
                    return GRAccessObjectKind.Instance;
                default:
                    throw new ArgumentException("Object type must be one of: all, template, instance.");
            }
        }

        private static string GetArg(IDictionary<string, string> args, string key, string defaultValue)
        {
            return args != null && args.TryGetValue(key, out var value) ? value : defaultValue;
        }

        private static bool GetBoolArg(IDictionary<string, string> args, string key)
        {
            return args != null
                && args.TryGetValue(key, out var value)
                && bool.TryParse(value, out var parsed)
                && parsed;
        }
    }
}
