using System.IO.Abstractions;
using System.Text.Json;
using Quadarax.Foundation.Core.Logging;
using Quadarax.Foundation.Core.QConsole;
using Quadarax.Foundation.Core.QConsole.Argument;
using Quadarax.Foundation.Core.QConsole.Command.Base;
using Quadarax.Foundation.Core.QConsole.Value;
using Quadarax.Foundation.Core.QMonitor.Console.Options;
using Quadarax.Foundation.Core.Value;

namespace Quadarax.Foundation.Core.QMonitor.Console.Commands
{
    internal abstract class BaseCommand : AbstractCommand, ILogHandler
    {
        #region *** Constants ***

        protected const string CS_ARG_NAME_DUMP = "dump";
        private const string CS_ARG_HINT_DUMP = "<is_dump_reponses>";
        private const string CS_ARG_DESC_DUMP = "Flag if set dumps incomming json messages.";

        //protected const string CS_ARG_NAME_DEBUG = "dbg";
        //private const string CS_ARG_HINT_DEBUG = "<is_debug_mode>";
        //private const string CS_ARG_DESC_DEBUG = "Flag if set writes debug messages.";

        protected const string CS_ARG_NAME_URIGENERAL = "uri-gen";
        private const string CS_ARG_HINT_URIGENERAL = "<general_uri>";
        private const string CS_ARG_DESC_URIGENERAL = "Uri listener specification for receiving general metadata. If not set use default.";

        protected const string CS_ARG_NAME_URIDATA = "uri-dat";
        private const string CS_ARG_HINT_URIDATA = "";
        private const string CS_ARG_DESC_URIDATA = "Uri listener specification for receiving data. If not set use default.";

        protected const string CS_ARG_NAME_PURGE = "purge";
        private const string CS_ARG_HINT_PURGE = "<purge_cached_data>";
        private const string CS_ARG_DESC_PURGE = "Purges chached data before start monitoring.";


        #endregion

        #region *** Properties ***
        protected Configuration Configuration { get; private set; }

        protected bool IsDebug { get; private set; }
        protected IFileSystem FileSystem { get; private set; }

        protected QMonReceiver Receiver { get; private set; }

        #endregion

        #region *** Private fields ***
        private Uri? _uriGeneral;
        private Uri? _uriData;
        #endregion

        protected BaseCommand(Engine engine) : base(engine)
        {

        }

        protected override void OnInitialize()
        {
            base.OnInitialize();
            Configuration? conf;
            if (!FileSystem.File.Exists(Constants.CS_CONFIGURATION_FILE))
            {
                WriteWarning($"Configuration file '{Constants.CS_CONFIGURATION_FILE}' not found. Use default.");
                conf = new Configuration();
            }
            else
            {
                conf = JsonSerializer.Deserialize<Configuration>(
                    FileSystem.File.ReadAllText(Constants.CS_CONFIGURATION_FILE));
                if (conf == null)
                {
                    conf = new Configuration();
                    WriteWarning($"Configuration file '{Constants.CS_CONFIGURATION_FILE}' not found. Use default.");
                }
            }

            Configuration = conf;
            
        }

        protected virtual void OnDataReceived(MonData[] data)
        {
        }

        protected virtual void OnGeneralReceived(MonGeneral general)
        {
        }

        protected override void OnValidateArguments()
        {
            base.OnValidateArguments();

            IsDebug = Engine.Configuration.IsConsoleDebug;

            if (IsDebug)
                DebugConsoleWriter.IsWriteDebugEnabled = true;
                
            _uriGeneral = ContainsArgument(CS_ARG_NAME_URIGENERAL) ? GetUri(GetArgumentValueOrDefault<string>(CS_ARG_NAME_URIGENERAL)) : Configuration.GeneralUri;
            _uriData = ContainsArgument(CS_ARG_NAME_URIDATA) ? GetUri(GetArgumentValueOrDefault<string>(CS_ARG_NAME_URIDATA)) : Configuration.DataUri;
            
        }

        protected override IEnumerable<AbstractArgument> OnSetupArguments()
        {
            FileSystem = new FileSystem();
            var args = base.OnSetupArguments().ToList();
            args.Add(new FlagArgument(CS_ARG_NAME_DUMP, CS_ARG_DESC_DUMP, CS_ARG_HINT_DUMP, false));
            //args.Add(new FlagArgument(CS_ARG_NAME_DEBUG, CS_ARG_DESC_DEBUG, CS_ARG_HINT_DEBUG, false));
            args.Add(new NamedArgument(CS_ARG_NAME_URIGENERAL, CS_ARG_DESC_URIGENERAL, CS_ARG_HINT_URIGENERAL, TypeValuesEnum.String,string.Empty, false));
            args.Add(new NamedArgument(CS_ARG_NAME_URIDATA, CS_ARG_DESC_URIDATA, CS_ARG_HINT_URIDATA, TypeValuesEnum.String,string.Empty, false));
            args.Add(new FlagArgument(CS_ARG_NAME_PURGE, CS_ARG_DESC_PURGE, CS_ARG_HINT_PURGE, false));
            return args;
        }


        public void Log(LogSeverityEnum type, string message, Exception e = null)
        {
            if ((type == LogSeverityEnum.Trace || type == LogSeverityEnum.Debug) && !IsDebug)
                return;
            WriteInfo($"{type}: {message}" + (e == null ? "" : e.Message));
        }



        private Uri? GetUri(string uriAsString)
        {
            if (string.IsNullOrEmpty(uriAsString))
                return null;
            if (!Uri.TryCreate(uriAsString, UriKind.Absolute, out var result))
                return null;
            return result;
        }

        protected override void BeginExecute()
        {
            base.BeginExecute();
            var rcvCfg = QMonReceiverConfiguration.CreateDefault();
            rcvCfg.SourceUriGeneral = _uriGeneral ?? Configuration.GeneralUri;
            rcvCfg.SourceUriData = _uriData ?? Configuration.DataUri;
            WriteInfo($"Starting listener for general ({rcvCfg.SourceUriGeneral}) and data ({rcvCfg.SourceUriData}) ...");
            Receiver = new QMonReceiver(this.FileSystem, rcvCfg,GetArgumentValueOrDefault<bool>(CS_ARG_NAME_PURGE), (IsDebug ? this.Engine.Configuration.DefaultLoggerFactory : null)!, OnGeneralReceived, OnDataReceived);
        }

        protected override Result EndExecute(Result incommingResult)
        {
            WriteInfo("Stopping listener...");
            Receiver?.Dispose();
            return base.EndExecute(incommingResult);
        }

    }
}
