using System;
using System.ComponentModel;
using System.Linq;

namespace InABox.Core
{

    public class CountryLink : EntityLink<Country>
    {
        [NullEditor]
        public override Guid ID { get; set; }
        
        [CodeEditor(Editable=Editable.Disabled)]
        [EditorSequence(1)]
        public string Code { get; set; }
        
        [Caption("Country")]
        [EditorSequence(2)]
        public string Name { get; set; }
    }
    
    public class Country : Entity, IRemotable, IPersistent
    {
        
        [UniqueCodeEditor(Editable=Editable.Enabled)]
        [EditorSequence(1)]
        public string Code { get; set; }
        
        [Caption("Country")]
        [EditorSequence(2)]
        public string Name { get; set; }
        
        static Country()
        {
            DefaultColumns.Add<State>(x => x.Code, 100);
            DefaultColumns.Add<Country>(x => x.Name, caption: "Country");
        }
    }

    public class StateLink : EntityLink<State>
    {
        [LookupEditor(typeof(State))]
        public override Guid ID { get; set; }

        [NullEditor]
        public CountryLink Country => InitializeField(ref _country);
        private CountryLink? _country;
        
        [CodeEditor(Editable = Editable.Disabled)]
        [EditorSequence(1)]
        public string Code { get; set; }
        
        [Caption("State")]
        [EditorSequence(2)]
        public string Name { get; set; }
    }
    
    public class State : Entity, IRemotable, IPersistent
    {

        [EntityRelationship(DeleteAction.Cascade)]
        [EditorSequence(1)]
        public CountryLink Country => InitializeField(ref _country);
        private CountryLink? _country;
        
        [UniqueCodeEditor(Editable = Editable.Enabled)]
        [EditorSequence(2)]
        public string Code { get; set; }
        
        [EditorSequence(3)]
        public string Name { get; set; }
        
        static State()
        {
            DefaultColumns.Add<State>(x => x.Code, 100);
            DefaultColumns.Add<State>(x => x.Name, caption: "State");
        }
        
    }

    public class LocalityLink : EntityLink<Locality>
    {
        public override Guid ID { get; set; }
        
        [EditorSequence(1)]
        public StateLink State => InitializeField(ref _state);
        private StateLink? _state;
        
        [EditorSequence(3)]
        public string Name { get; set; }
        
        [EditorSequence(4)]
        public string PostCode { get; set; }
    }

    public class Locality : Entity, IRemotable, IPersistent
    {
        [EntityRelationship(DeleteAction.Cascade)]
        [EditorSequence(1)]
        public StateLink State => InitializeField(ref _state);
        private StateLink? _state;
        
        [EditorSequence(3)]
        public string? Name { get; set; }
        
        [EditorSequence(4)]
        public string? PostCode { get; set; }

        static Locality()
        {
            DefaultColumns.Add<Locality>(x => x.Name, caption: "Locality");
            DefaultColumns.Add<Locality>(x => x.PostCode, 100);
        }
    }

    public enum LocalityType
    {
        Country,
        State,
        Locality
    }
    
    public interface ILocalitySummary
    {
        Guid ID { get; set; }
        Guid ParentID { get; set; }
        LocalityType Type { get; set; }
        string CountryCode { get; set; }
        string CountryName { get; set; }
        string StateCode { get; set; }
        string StateName { get; set; }
        string LocalityCode { get; set; }
        string LocalityName { get; set; }
        string Name { get; set; }
        string Code { get; set; }
    }
    
    public class LocalitySummaryGenerator : AutoEntityUnionGenerator<ILocalitySummary>
    {
        protected override void Configure()
        {
            AddTable<Locality>()
                .AliasField(x => x.ParentID, x => x.State.ID)
                .AliasField(x =>x.CountryCode, x=>x.State.Country.Code)
                .AliasField(x =>x.CountryName, x=>x.State.Country.Name)
                .AliasField(x =>x.StateCode, x=>x.State.Code)
                .AliasField(x =>x.StateName, x=>x.State.Name)
                .AliasField(x =>x.LocalityCode, x=>x.PostCode)
                .AliasField(x =>x.LocalityName, x=>x.Name)
                .AliasField(x =>x.Code, x => x.PostCode)
                .AddConstant(x=>x.Type, LocalityType.Locality);
            
            AddTable<State>()
                .AliasField(x=>x.ParentID, x => x.Country.ID)
                .AliasField(x =>x.CountryCode, x=>x.Country.Code)
                .AliasField(x =>x.CountryName, x=>x.Country.Name)
                .AliasField(x =>x.StateCode, x=>x.Code)
                .AliasField(x =>x.StateName, x=>x.Name)
                .AddConstant(x=>x.LocalityCode, "")
                .AddConstant(x=>x.LocalityName, "")
                .AddConstant(x=>x.Type, LocalityType.State);
            
            AddTable<Country>()
                .AddConstant(x => x.ParentID, Guid.Empty)
                .AliasField(x =>x.CountryCode, x=>x.Code)
                .AliasField(x =>x.CountryName, x=>x.Name)
                .AddConstant(x=>x.StateCode, "")
                .AddConstant(x=>x.StateName, "")
                .AddConstant(x=>x.LocalityCode, "")
                .AddConstant(x=>x.LocalityName, "")
                .AddConstant(x=>x.Type, LocalityType.Country);
        }

        public override bool Distinct => false;

        public override Column<ILocalitySummary>[] IDColumns => Columns.None<ILocalitySummary>().Add(x => x.ID).ToArray();
    }

    
    [AutoEntity(typeof(LocalitySummaryGenerator))]
    public class LocalitySummary : Entity, IRemotable, IPersistent, ILocalitySummary
    {
        [NullEditor]
        public Guid ParentID { get; set; }
        
        [NullEditor]
        public LocalityType Type { get; set; }
        
        [EditorSequence(1)]
        public string Name { get; set; }
        
        [EditorSequence(2)]
        public string Code { get; set; }
        
        [EditorSequence(3)]
        public string CountryCode { get; set; }
        
        [EditorSequence(4)]
        public string CountryName { get; set; }
        
        [EditorSequence(5)]
        public string StateCode { get; set; }
        
        [EditorSequence(6)]
        public string StateName { get; set; }
       
        [EditorSequence(7)]
        public string LocalityCode { get; set; }
        
        [EditorSequence(8)]
        public string LocalityName { get; set; }

        static LocalitySummary()
        {
            DefaultColumns.Add<LocalitySummary>(x=>x.Name, caption: "Locality");
            DefaultColumns.Add<LocalitySummary>(x => x.Code, 100, "Code");
        }
    }

    public class Address : EnclosedEntity, IPersistent, IRemotable
    {
        [EditorSequence(1)]
        [MemoEditor]
        public string Street { get; set; } = "";

        [EditorSequence(2)] public LocalityLink LocalityLink => InitializeField(ref _locality);
        private LocalityLink _locality;
        
        [EditorSequence(2)]
        [TextBoxEditor]
        public string City { get; set; } = "";

        [EditorSequence(3)]
        [ComboLookupEditor(typeof(StateLookups))]
        public string State { get; set; } = "";

        [EditorSequence(4)]
        [CodeEditor(Editable = Editable.Enabled)]
        public string PostCode { get; set; } = "";

        [NullEditor]
        public Location Location => InitializeField(ref _location, nameof(Location));
        private Location? _location;

        private class StateLookups : LookupGenerator<object>
        {
            public StateLookups(object[] items) : base(items)
            {
                AddValue("Australian Capital Territory", "Australian Capital Territory");
                AddValue("New South Wales", "New South Wales");
                AddValue("Northern Territory", "Northern Territory");
                AddValue("Queensland", "Queensland");
                AddValue("South Australia", "South Australia");
                AddValue("Tasmania", "Tasmania");
                AddValue("Victoria", "Victoria");
                AddValue("Western Australia", "Western Australia");
            }
        }

        public void CopyFrom(Address address)
        {
            Street = address.Street;
            City = address.City;
            State = address.State;
            PostCode = address.PostCode;
            Location.CopyFrom(address.Location);
        }
    }
}