using Newtonsoft.Json;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.InteropServices;

namespace InABox.Core
{
    [UserTracking(typeof(User))]
    public class CustomProperty : Entity, IProperty, IPersistent, IRemotable, ISequenceable, ILicense<CoreLicense>
    {
        public Type? _class;

        private Func<object, object> _getter;

        private string _name = "";

        private Action<object, object?> _setter;

        private string _type;

        private Type type = typeof(object);

        private string _caption;
        private long _sequence;
        private string _page;
        private bool _visible;
        private bool _editable;
        
        private Summary _summary;
        
        [ComboLookupEditor(typeof(PropertyClassLookups))]
        [EditorSequence(1)]
        public string Class
        {
            get => _class != null ? _class.EntityName() : "";
            set
            {
                CoreUtils.TryGetEntity(value, out _class);
                CheckExpressions();
            }
        }
        
        [EditorSequence(2)]
        public string Name
        {
            get => _name;
            set
            {
                _name = value;
                CheckExpressions();
                RegenerateEditor();
            }
        }

        [ComboLookupEditor(typeof(PropertyTypeLookups), Visible = Core.Visible.Default)]
        [EditorSequence(3)]
        public string Type
        {
            get => _type;
            set
            {
                _type = value;
                try
                {
                    type = CoreUtils.GetEntityOrNull(_type) ?? typeof(object);
                }
                catch
                {
                    type = typeof(object);
                }
                CheckExpressions();

                IsEntityLink = type.GetInterfaces().Contains(typeof(IEntityLink));
                IsEnclosedEntity = type.GetInterfaces().Contains(typeof(IEnclosedEntity));
                IsParent = IsEntityLink || IsEnclosedEntity;

                RegenerateEditor();
            }
        }
        
        [MemoEditor(Visible = Core.Visible.Default)]
        [EditorSequence(4)]
        public string Comment { get; set; }
        
        [TextBoxEditor(Visible = Core.Visible.Optional)]
        [EditorSequence(5)]
        public string Caption
        {
            get => _caption;
            set
            {
                _caption = value;
                RegenerateEditor();
            }
        }

        [TextBoxEditor(Visible = Core.Visible.Optional)]
        [EditorSequence(6)]
        public string Page
        {
            get => _page;
            set
            {
                _page = value;
                RegenerateEditor();
            }
        }

        [CheckBoxEditor(Visible = Core.Visible.Optional)]
        [EditorSequence(7)]
        public bool Required { get; set; }

        [CheckBoxEditor(Visible = Core.Visible.Default)]
        [EditorSequence(8)]
        public bool Visible
        {
            get => _visible;
            set
            {
                _visible = value;
                RegenerateEditor();
            }
        }

        [CheckBoxEditor(Visible = Core.Visible.Optional)]
        [EditorSequence(9)]
        public bool Editable
        {
            get => _editable;
            set
            {
                _editable = value;
                RegenerateEditor();
            }
        }

        [EnumLookupEditor(typeof(Summary))]
        [EditorSequence(10)]
        public Summary Summary
        {
            get => _summary;
            set
            {
                _summary = value;
                RegenerateEditor();
            }
        }


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

            Visible = true;
            Editable = true;
            Required = false;
            HasEditor = true;
            Editor = new NullEditor();
            _summary = Summary.None;
        }

        


        [DoNotPersist]
        [DoNotSerialize]
        public Type? ClassType
        {
            get => _class;
            set
            {
                _class = value;
                CheckExpressions();
            }
        }
        
        [DoNotPersist]
        [DoNotSerialize]
        public Type PropertyType
        {
            get => type;
            set
            {
                type = value;
                _type = value.EntityName();
                Editor = null;
                IsEntityLink = type.GetInterfaces().Contains(typeof(IEntityLink));
                IsEnclosedEntity = type.GetInterfaces().Contains(typeof(IEnclosedEntity));
                IsParent = IsEntityLink || IsEnclosedEntity;

                RegenerateEditor();
            }
        }

        [NullEditor]
        [DoNotPersist]
        [DoNotSerialize]
        public BaseEditor Editor { get; set; }

        [NullEditor]
        [DoNotPersist]
        [DoNotSerialize]
        public bool HasEditor { get; set; }

        

        [NullEditor]
        public LoggablePropertyAttribute? Loggable { get; set; }

        [NullEditor]
        public long Sequence
        {
            get => _sequence;
            set
            {
                _sequence = value;
                RegenerateEditor();
            }
        }

        [NullEditor]
        [DoNotPersist]
        [DoNotSerialize]
        public IProperty? Parent { get; set; }

        [NullEditor]
        [DoNotPersist]
        [DoNotSerialize]
        public bool IsEntityLink { get; set; }

        [NullEditor]
        [DoNotPersist]
        [DoNotSerialize]
        public bool IsEnclosedEntity { get; set; }

        [NullEditor]
        [DoNotPersist]
        [DoNotSerialize]
        public bool IsParent { get; set; }

        [NullEditor]
        [DoNotPersist]
        [DoNotSerialize]
        public bool IsCalculated => false;

        [NullEditor]
        [DoNotPersist]
        [DoNotSerialize]
        public bool IsPersistable => true;

        [NullEditor]
        [DoNotPersist]
        [DoNotSerialize]
        public bool IsSerializable => true;

        [NullEditor]
        [DoNotPersist]
        [DoNotSerialize]
        public bool IsDBColumn => true;

        private static string? NonWhiteSpaceOrNull(string? name)
        {
            return string.IsNullOrWhiteSpace(name) ? null : name;
        }

        public TAttribute? GetAttribute<TAttribute>() where TAttribute : Attribute
        {
            return null;
        }

        private void RegenerateEditor()
        {
            Editor = EditorUtils.GetEditor(PropertyType) ?? new NullEditor();
            Editor.Caption = NonWhiteSpaceOrNull(Caption) ?? NonWhiteSpaceOrNull(Name) ?? string.Empty;
            Editor.EditorSequence = (int)Sequence;
            Editor.Page = Page;
            Editor.Editable = Editable ? Core.Editable.Enabled : Core.Editable.Disabled;
            Editor.Visible = Visible ? Core.Visible.Optional : Core.Visible.Hidden;
            Editor.Summary = Summary;
        }

        public Expression Expression()
        {
            if (_class is null)
                throw new Exception("No class for CustomProperty");
            return CoreUtils.CreateIndexExpression(_class, "UserProperties", Name);
        }

        public Func<object, object> Getter()
        {
            return _getter;
        }

        Func<object, object> IProperty.NullSafeGetter() => Getter();

        public Action<object, object?> Setter()
        {
            return _setter;
        }

        public override string ToString()
        {
            return string.Format("{0}.{1}", Class, Name);
        }

        private void CheckExpressions()
        {
            if (_class == null)
                return;
            if (string.IsNullOrEmpty(Name))
                return;
            try
            {
                _getter = Expressions.Getter(_class, "UserProperties", Name);
                _setter = Expressions.Setter(_class, "UserProperties", Name);
            }
            catch (Exception e)
            {
                Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
            }
        }

        public decimal PropertySequence() => Sequence;
    }

    public class CustomPropertyLookups : EntityLookup<CustomProperty>
    {
        public override Columns<CustomProperty> DefineColumns()
        {
            return base.DefineColumns()
                .Add(x => x.Class)
                .Add(x => x.Name);
        }

        //public string FormatLookup(Dictionary<string, object> values, IEnumerable<string> exclude)
        //{
        //    return LookupFactory.DefaultFormatLookup(values, exclude.Concat(new String[] { "ID" }));
        //}

        public override Filter<CustomProperty>? DefineFilter()
        {
            return null;
        }

        public override SortOrder<CustomProperty> DefineSortOrder()
        {
            return new SortOrder<CustomProperty>(x => x.Class).ThenBy(x => x.Sequence);
        }
    }
}