using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;
using InABox.Configuration;
using InABox.Core;

namespace InABox.DynamicGrid
{
    public interface IDynamicEnclosedListGrid<TOne, TMany> : IDynamicEditorPage
    {
        TOne Entity { get; set; }
        List<TMany> Items { get; }
        void LoadItems(IEnumerable<TMany> items);
    }

    public class DynamicEnclosedListGrid<TOne, TMany> : DynamicGrid<TMany>, IDynamicEditorPage, IDynamicEnclosedListGrid<TOne, TMany>
        where TMany : BaseObject, new() where TOne : Entity, IRemotable, IPersistent, new()
    {
        private readonly List<TMany> MasterList = new();

        private readonly PropertyInfo property;

        public PageType PageType => PageType.Other;

        public DynamicEnclosedListGrid(PropertyInfo prop)
        {
            Items = new List<TMany>();
            property = prop;
            Options.AddRange(DynamicGridOption.RecordCount, DynamicGridOption.SelectColumns);
        }

        public DynamicEditorGrid EditorGrid { get; set; }

        public bool Ready { get; set; }

        public string Caption()
        {
            var caption = typeof(TMany).GetCustomAttribute(typeof(Caption));
            if (caption != null)
                return ((Caption)caption).Text;
            var result = new Inflector.Inflector(new CultureInfo("en")).Pluralize(typeof(TMany).Name);
            return result;
        }

        public virtual int Order()
        {
            return int.MinValue;
        }

        public void Load(object item, Func<Type, CoreTable>? PageDataHandler)
        {
            Entity = (TOne)item;
            MasterList.Clear();
            var list = property.GetValue(item) as IList;
            if (list != null)
                foreach (var entry in list)
                    MasterList.Add((TMany)entry);
            Items.AddRange(MasterList);
            Refresh(true, true);
            Ready = true;
        }

        public void BeforeSave(object item)
        {
            var list = Activator.CreateInstance(property.PropertyType) as IList;
            foreach (var entry in Items)
                list.Add(entry);
            property.SetValue(item, list);
        }

        public void AfterSave(object item)
        {
        }

        public Size MinimumSize()
        {
            return new Size(400, 400);
        }

        public List<TMany> Items { get; private set; }

        public TOne Entity { get; set; }

        public void LoadItems(IEnumerable<TMany> items)
        {
            Items.Clear();
            if (items != null)
                Items.AddRange(items);
            Refresh(false, true);
        }

        protected override DynamicGridColumns LoadColumns()
        {
            var tag = typeof(TMany).Name;

            var global = Task.Run(() => new GlobalConfiguration<DynamicGridColumns>(tag).Load());
            var user = Task.Run(() => new UserConfiguration<DynamicGridColumns>(tag).Load());
            Task.WaitAll(global, user);
            var columns = user.Result.Any() ? user.Result : global.Result;

            if (columns.Count == 0)
                columns.AddRange(base.LoadColumns().Where(x => !x.ColumnName.StartsWith(property.Name)));
            return columns;
        }

        protected override void SaveColumns(DynamicGridColumns columns)
        {
            var tag = typeof(TMany).Name;
            new UserConfiguration<DynamicGridColumns>(tag).Save(columns);
        }

        protected override TMany LoadItem(CoreRow row)
        {
            return Items[row.Index];
        }

        protected override TMany[] LoadItems(CoreRow[] rows)
        {
            var result = new List<TMany>();
            foreach (var row in rows)
                result.Add(Items[row.Index]);
            return result.ToArray();
        }

        public override void SaveItem(TMany item)
        {
            if (!Items.Contains(item))
                Items.Add(item);

            if (item is ISequenceable)
            {
                //var exp = BaseObject.DefaultSortOrder<TMany>().Expression;
                var exp = LookupFactory.DefineSort<TMany>().Expression;
                Items = Items.AsQueryable().SortBy(exp).ToList();
            }
        }

        protected override void DeleteItems(params CoreRow[] rows)
        {
            foreach (var row in rows.OrderByDescending(x => x.Index))
                Items.RemoveAt(row.Index);
        }

        protected override void Reload(Filters<TMany> criteria, Columns<TMany> columns, ref SortOrder<TMany>? sort,
            Action<CoreTable?, Exception?> action)
        {
            var results = new CoreTable();
            results.LoadColumns(typeof(TMany));

            if (sort != null)
            {
                var exp = IQueryableExtensions.ToLambda<TMany>(sort.Expression);
                var sorted = sort.Direction == SortDirection.Ascending
                    ? Items.AsQueryable().OrderBy(exp)
                    : Items.AsQueryable().OrderByDescending(exp);
                foreach (var then in sort.Thens)
                {
                    var thexp = IQueryableExtensions.ToLambda<TMany>(then.Expression);
                    sorted = sort.Direction == SortDirection.Ascending ? sorted.ThenBy(exp) : sorted.ThenByDescending(exp);
                }

                results.LoadRows(sorted);
            }
            else
            {
                results.LoadRows(Items);
            }

            //if (sort != null)
            //    results.LoadRows(Items.AsQueryable().SortBy(sort.Expression));
            //else
            //    results.LoadRows(Items.OrderBy(x => x.Sort));

            action.Invoke(results, null);
        }
    }
}