using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Mono.Options;
using Newtonsoft.Json;

namespace isn
{
    public partial class Program
    {

        public static IEnumerable<IsnSourceSettings> Sources { get; protected set; }

        public static void LoadConfig()
        {
            FileInfo cfgSettingIf = new FileInfo(_configFileName);
            if (cfgSettingIf.Exists)
            {
                var json = File.ReadAllText(cfgSettingIf.FullName);
                settings = JsonConvert.DeserializeObject<Settings>(json);
                source = settings.DefaultSource;
            }
        }
        static OptionSet storeoptions = new OptionSet {
            { "s|source=", "use source", val => source = source ?? val },
            { "h|help", "show this message and exit", h => shouldShowPushHelp = h != null },
        };
        private static string _configFileName =
        Path.Combine(
        Path.Combine(Environment.GetFolderPath(
            Environment.SpecialFolder.UserProfile), ".isn"),
            "config.json")
            ;

        public const string push = "push";
        static OptionSet options = new OptionSet {
                { "h|help", "show this message and exit", h => shouldShowHelp = h != null },
            };

        static OptionSet pushoptions = new OptionSet {
                { "k|api-key=", "use api key", val => apiKey = apiKey ?? val },
                { "p|store-api-key", "store used api key (=<true|false>)", val => storApiKey = val != null },
                { "s|source=", "use source", val => source = source ?? val },
                { "h|help", "show this message and exit", h => shouldShowPushHelp = h != null },
            };
        static OptionSet sourceoptions = new OptionSet {
                { "h|help", "show this message and exit", h => shouldShowSourceHelp = h != null },
            };

        static OptionSet showOptions = new OptionSet {
                { "h|help", "show this message and exit", h => shouldShowSourceHelp = h != null },
            };

        private static bool shouldShowHelp;
        private static bool shouldShowSourceHelp;
        private static bool shouldShowPushHelp;
        private static string apiKey = null;
        private static string source = null;
        private static int pushKO = 0;
        private static bool storApiKey = false;
        public static IDataProtector Protector { get; set; } = new DefaultDataProtector();

        static Settings settings = null;


        public static Settings Settings
        {
            get
            {
                if (settings == null)
                    LoadConfig();
                if (settings == null)
                    settings = new Settings
                    {
                        DataProtectionTitle = "isn",
                        Sources = new Dictionary<string, SourceSettings>()
                    };
                return settings;
            }
        }

        static int Main(string[] args)
        {
            var commandSet = new CommandSet("isn");
            var srclst = new Command("list")
            {
                Run = sargs => SourceList(sargs)
            };

            var showconfig = new Command("config")
            {
                Run = sargs => ShowConfig()
            };

            var srcadd = new Command("add")
            {
                Run = sargs => SourceAdd(sargs)
            };

            var srcsetdef = new Command("set-default")
            {
                Run = sargs => SetDefaultSource(sargs?.FirstOrDefault())
            };

            var showCommand = new Command("show")
            {
                Run = sargs =>
                {
                    var showCommandSet = new CommandSet("show")
                    {
                        showconfig
                    };
                    var pargs = showOptions.Parse(sargs);
                    if (shouldShowSourceHelp)
                    {
                        // output the options
                        Console.WriteLine("Sources Options:");
                        showOptions.WriteOptionDescriptions(Console.Out);
                        return;
                    }
                    showCommandSet.Run(pargs);
                }
            };

            var srcCmd = new Command("sources")
            {
                Run = sargs =>
                {

                    var sourcesCmdSet = new CommandSet("sources")
                    {
                        srclst,
                        srcadd,
                        srcsetdef
                    };


                    var pargs = sourceoptions.Parse(sargs);
                    if (shouldShowSourceHelp)
                    {
                        // output the options
                        Console.WriteLine("Sources Options:");
                        sourceoptions.WriteOptionDescriptions(Console.Out);
                        return;
                    }
                    sourcesCmdSet.Run(pargs);
                }
            };

            var add = new Command("add")
            {
                Run = sargs => Add(sargs)
            };
            commandSet.Add(add);

            var pushCmd = new Command(push)
            {
                Run = async sargs =>
                {
                    var pargs = pushoptions.Parse(sargs);
                    if (shouldShowPushHelp)
                    {
                        // output the options
                        Console.Error.WriteLine("Push Options:");
                        pushoptions.WriteOptionDescriptions(Console.Out);
                        return;
                    }
                    List<PushReport> reports = PushPkg(pargs);
                    Console.WriteLine(JsonConvert.SerializeObject(reports));
                    pushKO = reports.Count(r => !r.OK && !r.AlreadyPresent);
                }
            };

            var setapikey = new Command("set-api-key")
            {
                Run = sargs => StoreApiKey(sargs)
            };
            commandSet.Add(pushCmd);
            commandSet.Add(setapikey);
            commandSet.Add(srcCmd);
            commandSet.Add(showCommand);

            List<string> extra;
            try
            {
                // parse the command line
                extra = options.Parse(args);
            }
            catch (OptionException e)
            {
                // output some error message
                Console.Write("isn: ");
                Console.WriteLine(e.Message);
                Console.WriteLine("Try `isn --help' for more information.");
                return 2;
            }


            if (shouldShowHelp)
            {
                // output the options
                Console.WriteLine("Options:");
                options.WriteOptionDescriptions(Console.Out);
                return 1;
            }

            int runCode = commandSet.Run(args);
            return (runCode == 0 && pushKO > 0) ? 500 : runCode;

        }
    }
}
