using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Quadarax.Foundation.Core.Data.Interface;
using Quadarax.Foundation.Core.Data.Interface.Entity;
using Quadarax.Foundation.Core.Object.Extensions;
using Quadarax.Foundation.Core.QConsole;
using Quadarax.Foundation.Core.QConsole.Argument;
using Quadarax.Foundation.Core.QConsole.Value;
using Quadarax.Foundation.Core.Value.Extensions;

namespace BO.Console.Commands.Base
{
    internal abstract class AbstractListCommand : BaseCommand
    {
        #region *** Constants ***

        private const string CS_ARG_NAME_NOPAGING = "nopg";
        private const string CS_ARG_HINT_NOPAGING = "<is_pg_disabled>";
        private const string CS_ARG_DESC_NOPAGING = "Flag if paging is disabled.";

        private const string CS_ARG_NAME_PAGE = "pg";
        private const string CS_ARG_HINT_PAGE = "<page>";
        private const string CS_ARG_DESC_PAGE = "Zero index page number.";

        private const string CS_ARG_NAME_PAGESIZE = "pgsize";
        private const string CS_ARG_HINT_PAGESIZE = "<page_size>";
        private const string CS_ARG_DESC_PAGESIZE = "Items count one per page. If not defined uses default from config.";
        #endregion

        #region *** Private Fields ***
        protected PagingDto Paging { get; private set; }
        #endregion

        #region *** Constructors ***
        protected AbstractListCommand(Engine engine) : base(engine)
        {
        }
        #endregion


        #region *** Protected Overrides ***

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

        }

        protected override void OnValidateArguments()
        {
            // TODO :optimize
            base.OnValidateArguments();
            if (WasArgumentSpecified(CS_ARG_NAME_NOPAGING))
            {
                Paging = new PagingDto();
                WriteCaption("Paging: Disabled.");
                return;
            }
            var page = GetArgumentValueOrDefault<int>(CS_ARG_NAME_PAGE);
            var pageSize = GetArgumentValueOrDefault<int>(CS_ARG_NAME_PAGESIZE);
            pageSize = pageSize == 0 ? Configuration.DefaultPageSize : pageSize;
            Paging = new PagingDto(page, pageSize);


            WriteCaption(Paging.IsDisabled
                ? "Paging: Disabled."
                : $"Paging: {Paging.Page} - {Paging.PageSize} items per page.");
        }

        protected override IEnumerable<AbstractArgument> OnSetupArguments()
        {
            var args =  new List<AbstractArgument>(base.OnSetupArguments());
            args.Add(new FlagArgument(CS_ARG_NAME_NOPAGING, CS_ARG_DESC_NOPAGING, CS_ARG_HINT_NOPAGING, false));
            args.Add(new NamedArgument(CS_ARG_NAME_PAGE, CS_ARG_DESC_PAGE, CS_ARG_HINT_PAGE,TypeValuesEnum.Integer,"0",false));
            args.Add(new NamedArgument(CS_ARG_NAME_PAGESIZE, CS_ARG_DESC_PAGESIZE, CS_ARG_HINT_PAGESIZE,TypeValuesEnum.Integer,string.Empty,false));
            return args;
        }

        protected void WriteDtoToConsole(IEnumerable<IDto> dtos, params string[] propertiesToShow)
        {
            // TODO : optimize

            if (dtos == null || !dtos.Any())
            {
                WriteInfo("No data.");
                return;
            }

            var propLengths = new Dictionary<string, int>();
            if (!(propertiesToShow == null || propertiesToShow.Length > 0))
            {
                propertiesToShow = dtos.First().GetAllPropertyValues().Select(x => x.Key).ToArray();
            }

            foreach (var propertyName in propertiesToShow)
                propLengths.Add(propertyName, propertyName.Length + 1);

            var dtosProps = new List<IDictionary<string, object>>();

            // gather data and column lengths
            foreach (var dto in dtos)
            {
                var props = dto.GetAllPropertyValues();
                dtosProps.Add(props);
                foreach (var prop in props)
                {
                    if (!propertiesToShow.Contains(prop.Key))
                        continue;

                    if (!propLengths.ContainsKey(prop.Key))
                        propLengths.Add(prop.Key,0);
                    var curLen = prop.Value == null ? 0 : prop.ToString().Length + 1;
                    if (curLen > propLengths[prop.Key]) propLengths[prop.Key] = curLen;
                }
            }

            // printout columns
            WriteInfo(string.Empty);
            var sb = new StringBuilder();
            foreach (var prop in propLengths)
                sb.Append(prop.Key.Align(prop.Value));
            WriteCaption(sb.ToString());
            WriteInfo(new string('-', propLengths.Sum(x=>x.Value)));

            // iterate columns values
            foreach (var dto in dtosProps)
            {
                sb = new StringBuilder();
                foreach (var prop in propLengths)
                {
                    var val = string.Empty;
                    val = dto[prop.Key]?.ToString();
                    sb.Append(val.Align(prop.Value));
                }

                WriteInfo(sb.ToString());
            }
            WriteInfo(string.Empty);
            WriteCaption($"Total {dtosProps.Count} items.");
        }


        #endregion
    }
}
