using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace InABox.Core
{
    
    public enum DFLayoutLookupDisplayType
    {
        Button = 0,
        Combo = 1,
    }
    
    public class DFLayoutLookupFieldProperties : DFLayoutFieldProperties<Guid, DFLayoutLookupValue>
    {
        
        [EditorSequence(-995)]
        [NullEditor]
        public override Guid Default { get; set; }
        
        [DoNotSerialize]
        [NullEditor]
        public List<string> AdditionalPropertiesList = new List<string>();

        [ComboLookupEditor(typeof(PropertyClassLookups))]
        [EditorSequence(100)]
        public string LookupType { get; set; } = "";

        // A JSON-serialized Filter for filtering entries in the lookup editor
        [FilterEditor]
        [EditorSequence(101)]
        public string Filter { get; set; } = "";

        /// <summary>
        /// A list of comma-separated property names to also pull with the lookup.
        /// </summary>
        //[TextBoxEditor(ToolTip = "A comma-separated list of property names.")]
        [MultiLookupEditor(typeof(AdditionalPropertyLookupGenerator))]
        [EditorSequence(102)]
        public string AdditionalProperties
        {
            get => string.Join(',', AdditionalPropertiesList);
            set
            {
                AdditionalPropertiesList = value.Split(',').ToList();
            }
        }
        
        [EnumLookupEditor(typeof(DFLayoutLookupDisplayType))]
        [EditorSequence(103)]
        public DFLayoutLookupDisplayType DisplayType { get; set; }

        public DFLayoutLookupFieldProperties()
        {
            DisplayType = DFLayoutLookupDisplayType.Button;
        }

        public override string FormatValue(DFLayoutLookupValue value)
        {
            return value.Text;
        }

        public override Guid GetValue(DFLayoutLookupValue value)
        {
            return value.ID;
        }

        public override DFLayoutLookupValue DeserializeValue(DFLoadStorageEntry entry)
        {
            var value = new DFLayoutLookupValue();
            value.Deserialize(entry);
            return value;
        }

        public override void SerializeValue(DFSaveStorageEntry entry, DFLayoutLookupValue value)
        {
            value.Serialize(entry);
        }

        public override IEnumerable<CoreColumn> GetAdditionalColumns()
        {
            if(!CoreUtils.TryGetEntity(LookupType, out var entity))
                return Enumerable.Empty<CoreColumn>();

            return AdditionalPropertiesList.Select(x =>
            {
                if (CoreUtils.TryGetProperty(entity, x, out var property))
                    return new CoreColumn
                    {
                        ColumnName = $"{Code}.{x}",
                        DataType = property.PropertyType
                    };
                return null;
            }).Where(x => x != null).Cast<CoreColumn>();
        }

        public override IEnumerable<KeyValuePair<string, object?>> GetAdditionalValues(DFLayoutLookupValue value)
        {
            if (!CoreUtils.TryGetEntity(LookupType, out var entity))
                return Enumerable.Empty<KeyValuePair<string, object?>>();

            return AdditionalPropertiesList
                .Where(x => CoreUtils.TryGetProperty(entity, x, out var property))
                .Select(x => new KeyValuePair<string, object?>($"{Code}.{x}", value.Values.GetValueOrDefault(x)));
        }

        public override IEnumerable<CoreColumn> GetDisplayColumns()
        {
            yield return new CoreColumn { ColumnName = Code, DataType = typeof(string) };
            yield return new CoreColumn { ColumnName = $"{Code}.ID", DataType = typeof(Guid) };
            foreach(var col in GetAdditionalColumns())
            {
                yield return col;
            }
        }
        public override IEnumerable<KeyValuePair<string, object?>> GetDisplayValues(DFLayoutLookupValue value)
        {
            yield return new KeyValuePair<string, object?>(Code, value.Text);
            yield return new KeyValuePair<string, object?>($"{Code}.ID", value.ID);
            foreach (var pair in GetAdditionalValues(value))
            {
                yield return pair;
            }
        }

        protected override void LoadProperties()
        {
            base.LoadProperties();
            LookupType = GetProperty(nameof(LookupType), "");
            Filter = GetProperty(nameof(Filter), "");
            AdditionalProperties = GetProperty(nameof(AdditionalProperties), "");
            DisplayType = GetProperty(nameof(DisplayType), DFLayoutLookupDisplayType.Button);
        }

        protected override void SaveProperties()
        {
            base.SaveProperties();
            SetProperty(nameof(LookupType), LookupType);
            SetProperty(nameof(Filter), Filter);
            SetProperty(nameof(AdditionalProperties), AdditionalProperties);
            SetProperty(nameof(DisplayType),DisplayType);
        }


        private class AdditionalPropertyLookupGenerator : LookupGenerator<object>
        {
            public AdditionalPropertyLookupGenerator(object[] items) : base(items)
            {
            }

            protected override void DoGenerateLookups()
            {
                if (!(Items?.FirstOrDefault() is DFLayoutLookupFieldProperties properties))
                    return;

                if (!CoreUtils.TryGetEntity(properties.LookupType, out var type))
                    return;

                var props = CoreUtils.PropertyList(type, x => x.GetCustomAttribute<DoNotSerialize>() == null, true).Keys.ToList();
                props.Sort();
                foreach (var prop in props)
                {
                    AddValue(prop, prop);
                }
            }
        }
    }
}