using AyCode.Core.Interfaces;
using AyCode.Core.Serializers.Attributes;
using AyCode.Core.Serializers.Binaries;

namespace AyCode.Core.Tests.TestModels;

/// <summary>
/// Test models for binary serializer tests.
/// </summary>
public static class AcSerializerModels
{
    [AcBinarySerializable(true)]
    public class TestSimpleClass
    {
        public int Id { get; set; }
        public string Name { get; set; } = "";
        public double Value { get; set; }
        public bool IsActive { get; set; }
    }

    public class TestNestedClass
    {
        public int Id { get; set; }
        public string Name { get; set; } = "";
        public TestSimpleClass? Child { get; set; }
    }

    public class TestClassWithList
    {
        public int Id { get; set; }
        public List<string> Items { get; set; } = new();
    }

    public class TestClassWithRepeatedStrings
    {
        [AcStringIntern(true)]
        public string Field1 { get; set; } = "";
        [AcStringIntern(true)]
        public string Field2 { get; set; } = "";
        public string Field3 { get; set; } = "";
        public string Field4 { get; set; } = "";
    }

    public class TestClassWithLongPropertyNames
    {
        public string FirstProperty { get; set; } = "";
        public string SecondProperty { get; set; } = "";
        public string ThirdProperty { get; set; } = "";
        public string FourthProperty { get; set; } = "";
    }

    public class TestClassWithMixedStrings
    {
        public int Id { get; set; }
        public string ShortName { get; set; } = "";
        public string LongName { get; set; } = "";
        public string Description { get; set; } = "";
        public string Tag { get; set; } = "";
    }

    public class TestNestedStructure
    {
        public string RootName { get; set; } = "";
        public List<TestLevel1> Level1Items { get; set; } = new();
    }

    public class TestLevel1
    {
        public string Level1Name { get; set; } = "";
        public List<TestLevel2> Level2Items { get; set; } = new();
    }

    public class TestLevel2
    {
        public string Level2Name { get; set; } = "";
        public string Value { get; set; } = "";
    }

    public class TestClassWithRepeatedValues
    {
        public int Id { get; set; }
        public string Status { get; set; } = "";
        public string Category { get; set; } = "";
        public string Priority { get; set; } = "";
    }

    public class TestClassWithNameValue
    {
        public string Name { get; set; } = "";
        public string Value { get; set; } = "";
    }

    public class TestClassWithNullableStrings
    {
        public int Id { get; set; }
        public string RequiredName { get; set; } = "";
        public string? OptionalName { get; set; }
        public string? Description { get; set; }
    }

    public class TestCustomerLikeDto
    {
        public int Id { get; set; }
        public string FirstName { get; set; } = "";
        public string LastName { get; set; } = "";
        public string Email { get; set; } = "";
        public string Company { get; set; } = "";
        public string Department { get; set; } = "";
        public string Role { get; set; } = "";
        public string Status { get; set; } = "";
        public List<TestAttribute> Attributes { get; set; } = new();
    }

    public class TestAttribute
    {
        public string Key { get; set; } = "";
        public string Value { get; set; } = "";
    }

    public class TestHighReuseDto
    {
        public int Id { get; set; }
        public string CategoryCode { get; set; } = "";
        public string StatusCode { get; set; } = "";
        public string TypeCode { get; set; } = "";
        public string PriorityCode { get; set; } = "";
        public string UniqueField { get; set; } = "";
    }

    public class TestClassWithNullableProperties
    {
        public int Id { get; set; }

        public int? NullableInt { get; set; }
        public int? NullableIntNull { get; set; }

        public long? NullableLong { get; set; }
        public long? NullableLongNull { get; set; }

        public double? NullableDouble { get; set; }
        public double? NullableDoubleNull { get; set; }

        public decimal? NullableDecimal { get; set; }
        public decimal? NullableDecimalNull { get; set; }

        public DateTime? NullableDateTime { get; set; }
        public DateTime? NullableDateTimeNull { get; set; }

        public Guid? NullableGuid { get; set; }
        public Guid? NullableGuidNull { get; set; }

        public bool? NullableBool { get; set; }
        public bool? NullableBoolNull { get; set; }
    }

    public class TestParentWithNullableChild
    {
        public int Id { get; set; }
        public string Name { get; set; } = "";
        public TestClassWithNullableProperties? Child { get; set; }
    }

    /// <summary>
    /// Test model mimicking StockTaking entity structure.
    /// /// </summary>
    public class TestStockTaking
    {
        public int Id { get; set; }
        public DateTime StartDateTime { get; set; }
        public bool IsClosed { get; set; }
        public int Creator { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
        public List<TestStockTakingItem>? StockTakingItems { get; set; }
    }

    /// <summary>
    /// Test model mimicking StockTakingItem entity structure.
    /// /// </summary>
    public class TestStockTakingItem
    {
        public int Id { get; set; }
        public int StockTakingId { get; set; }
        public int ProductId { get; set; }
        public bool IsMeasured { get; set; }
        public int OriginalStockQuantity { get; set; }
        public int MeasuredStockQuantity { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
        public List<TestStockTakingItemPallet>? StockTakingItemPallets { get; set; }
    }

    /// <summary>
    /// Test model mimicking StockTakingItemPallet/MeasuringItemPalletBase entity structure.
    /// Contains nullable int properties (CreatorId, ModifierId) that caused the production bug.
    /// </summary>
    public class TestStockTakingItemPallet
    {
        public int Id { get; set; }
        public int StockTakingItemId { get; set; }
        public int TrayQuantity { get; set; }
        public double TareWeight { get; set; }
        public double PalletWeight { get; set; }
        public double GrossWeight { get; set; }
        public bool IsMeasured { get; set; }

        public int? CreatorId { get; set; }
        public int? ModifierId { get; set; }

        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
    }

    public class TestEntityWithDateTimeAndInt
    {
        public int Id { get; set; }
        public int IntValue { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
        public int StatusCode { get; set; }
        public string Name { get; set; } = "";
    }

    public class TestEntityWithManyIntsBeforeDateTime
    {
        public int Id { get; set; }
        public int Value1 { get; set; }
        public int Value2 { get; set; }
        public int Value3 { get; set; }
        public int Value4 { get; set; }
        public int Value5 { get; set; }
        public DateTime FirstDateTime { get; set; }
        public DateTime SecondDateTime { get; set; }
        public int FinalValue { get; set; }
    }

    public class TestParentEntityWithDateTimeChild
    {
        public int ParentId { get; set; }
        public string ParentName { get; set; } = "";
        public TestEntityWithDateTimeAndInt? Child { get; set; }
    }

    public class TestParentWithDateTimeItemCollection
    {
        public int Id { get; set; }
        public string Name { get; set; } = "";
        public DateTime Created { get; set; }
        public List<TestEntityWithDateTimeAndInt>? Items { get; set; }
    }

    /// <summary>
    /// Base class to simulate inheritance hierarchy like MgEntityBase.
    /// Id property is in base class, so reflection may return it last.
    /// </summary>
    public abstract class TestEntityBase
    {
        public int Id { get; set; }
    }

    /// <summary>
    /// Simulates MgStockTaking structure with inheritance.
    /// Properties are ordered: StartDateTime, IsClosed, StockTakingItems, Creator, Created, Modified
    /// Id comes from base class and may appear last in reflection.
    /// </summary>
    public class TestStockTakingWithInheritance : TestEntityBase
    {
        public DateTime StartDateTime { get; set; }
        public bool IsClosed { get; set; }
        public List<TestStockTakingItemWithInheritance>? StockTakingItems { get; set; }
        public int Creator { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
    }

    /// <summary>
    /// Simulates StockTakingItem with inheritance.
    /// </summary>
    public class TestStockTakingItemWithInheritance : TestEntityBase
    {
        public int StockTakingId { get; set; }
        public int ProductId { get; set; }
        public bool IsMeasured { get; set; }
        public int OriginalStockQuantity { get; set; }
        public int MeasuredStockQuantity { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
        public List<TestStockTakingItemPalletWithInheritance>? StockTakingItemPallets { get; set; }
    }

    /// <summary>
    /// Simulates MeasuringItemPalletBase with inheritance and ForeignKey pattern.
    /// </summary>
    public class TestStockTakingItemPalletWithInheritance : TestEntityBase
    {
        public int StockTakingItemId { get; set; }
        public int TrayQuantity { get; set; }
        public double TareWeight { get; set; }
        public double PalletWeight { get; set; }
        public double GrossWeight { get; set; }
        public bool IsMeasured { get; set; }
        public int? CreatorId { get; set; }
        public int? ModifierId { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
    }

    #region Schema Mismatch Tests

    /// <summary>
    /// Server-side model with extra properties that client doesn't know about.
    /// </summary>
    public class ServerStockTaking
    {
        public int Id { get; set; }
        public DateTime StartDateTime { get; set; }
        public bool IsClosed { get; set; }
        public int Creator { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
        public List<ServerStockTakingItem>? StockTakingItems { get; set; }

        // Extra properties that client doesn't have
        public string? ExtraServerProperty1 { get; set; }
        public int ExtraServerProperty2 { get; set; }
    }

    /// <summary>
    /// Server-side item with Product navigation property.
    /// </summary>
    public class ServerStockTakingItem
    {
        public int Id { get; set; }
        public int StockTakingId { get; set; }
        public int ProductId { get; set; }
        public bool IsMeasured { get; set; }
        public int OriginalStockQuantity { get; set; }
        public int MeasuredStockQuantity { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }

        // Navigation property - this gets serialized on server!
        public ServerProductDto? Product { get; set; }

        public List<ServerStockTakingItemPallet>? StockTakingItemPallets { get; set; }
    }

    /// <summary>
    /// Server-side product DTO.
    /// </summary>
    public class ServerProductDto
    {
        public int Id { get; set; }
        public string Name { get; set; } = "";
        public string Description { get; set; } = "";
        public double Price { get; set; }
        public int CategoryId { get; set; }
    }

    /// <summary>
    /// Server-side pallet with all properties.
    /// </summary>
    public class ServerStockTakingItemPallet
    {
        public int Id { get; set; }
        public int StockTakingItemId { get; set; }
        public int TrayQuantity { get; set; }
        public double TareWeight { get; set; }
        public double PalletWeight { get; set; }
        public double GrossWeight { get; set; }
        public bool IsMeasured { get; set; }
        public int? CreatorId { get; set; }
        public int? ModifierId { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
    }

    /// <summary>
    /// Client-side model - MISSING some properties that server sends.
    /// </summary>
    public class ClientStockTaking
    {
        public int Id { get; set; }
        public DateTime StartDateTime { get; set; }
        public bool IsClosed { get; set; }
        public int Creator { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
        public List<ClientStockTakingItem>? StockTakingItems { get; set; }
        // Note: ExtraServerProperty1 and ExtraServerProperty2 are MISSING
    }

    /// <summary>
    /// Client-side item - MISSING Product navigation property.
    /// </summary>
    public class ClientStockTakingItem
    {
        public int Id { get; set; }
        public int StockTakingId { get; set; }
        public int ProductId { get; set; }
        public bool IsMeasured { get; set; }
        public int OriginalStockQuantity { get; set; }
        public int MeasuredStockQuantity { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
        // Note: Product property is MISSING - server sends it, client skips it
        public List<ClientStockTakingItemPallet>? StockTakingItemPallets { get; set; }
    }

    /// <summary>
    /// Client-side pallet.
    /// </summary>
    public class ClientStockTakingItemPallet
    {
        public int Id { get; set; }
        public int StockTakingItemId { get; set; }
        public int TrayQuantity { get; set; }
        public double TareWeight { get; set; }
        public double PalletWeight { get; set; }
        public double GrossWeight { get; set; }
        public bool IsMeasured { get; set; }
        public int? CreatorId { get; set; }
        public int? ModifierId { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
    }

    #endregion

    #region Circular Reference Test Models

    /// <summary>
    /// Parent entity with circular reference to child.
    /// </summary>
    public class CircularParent
    {
        public int Id { get; set; }
        public string Name { get; set; } = "";
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
        public int Creator { get; set; }
        public List<CircularChild>? Children { get; set; }
    }

    /// <summary>
    /// Child entity with back-reference to parent (circular).
    /// </summary>
    public class CircularChild
    {
        public int Id { get; set; }
        public int ParentId { get; set; }
        public string Name { get; set; } = "";
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }

        // Back-reference to parent - creates circular reference!
        public CircularParent? Parent { get; set; }

        public List<CircularGrandChild>? GrandChildren { get; set; }
    }

    /// <summary>
    /// Grandchild with back-reference.
    /// </summary>
    public class CircularGrandChild
    {
        public int Id { get; set; }
        public int ChildId { get; set; }
        public int? CreatorId { get; set; }
        public int? ModifierId { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }

        // Back-reference
        public CircularChild? Child { get; set; }
    }

    #endregion

    #region Generic Type Parameter Test Models

    /// <summary>
    /// Interface with fewer properties than implementation.
    /// </summary>
    public interface IGenericItem
    {
        int Id { get; set; }
        string Name { get; set; }
    }

    /// <summary>
    /// Implementation with MORE properties than interface.
    /// This is the exact pattern of StockTakingItem.
    /// </summary>
    public class GenericItemImpl : IGenericItem
    {
        public int Id { get; set; }
        public string Name { get; set; } = "";

        // Extra properties NOT in interface
        public int ExtraInt { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
        public string Description { get; set; } = "";
    }

    /// <summary>
    /// Parent class with generic type parameter for items.
    /// Similar to MgStockTaking&lt;TStockTakingItem&gt;.
    /// </summary>
    public class GenericParent<TItem> where TItem : class, IGenericItem
    {
        public int Id { get; set; }
        public string Name { get; set; } = "";
        public int Creator { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
        public List<TItem>? Items { get; set; }
    }

    /// <summary>
    /// Concrete implementation.
    /// Similar to StockTaking : MgStockTaking&lt;StockTakingItem&gt;.
    /// </summary>
    public class ConcreteParent : GenericParent<GenericItemImpl>
    {
    }

    #endregion

    #region Navigation Property Test Models

    /// <summary>
    /// Product entity that is NOT in the parent's generic type hierarchy.
    /// </summary>
    public class ProductEntity
    {
        public int Id { get; set; }
        public string Name { get; set; } = "";
        public string Description { get; set; } = "";
        public double Price { get; set; }
        public int CategoryId { get; set; }
        public DateTime Created { get; set; }
    }

    /// <summary>
    /// Item entity with navigation property to Product.
    /// This simulates StockTakingItem.Product.
    /// </summary>
    public class ItemWithNavigationProperty
    {
        public int Id { get; set; }
        public int ParentId { get; set; }
        public int ProductId { get; set; }
        public bool IsMeasured { get; set; }
        public int Quantity { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }

        // Navigation property - NOT in parent's generic type!
        // When populated, its properties need to be in metadata table too!
        public ProductEntity? Product { get; set; }
    }

    /// <summary>
    /// Parent entity with items that have navigation properties.
    /// This simulates StockTaking with StockTakingItems that have Product.
    /// </summary>
    public class ParentWithNavigatingItems
    {
        public int Id { get; set; }
        public string Name { get; set; } = "";
        public int Creator { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
        public List<ItemWithNavigationProperty>? Items { get; set; }
    }

    #endregion

    #region Property Order Test Models

    /// <summary>
    /// Simulates NopCommerce BaseEntity.
    /// </summary>
    public abstract class NopBaseEntity
    {
        public int Id { get; set; }
    }

    /// <summary>
    /// Simulates MgEntityBase : BaseEntity.
    /// </summary>
    public abstract class SimMgEntityBase : NopBaseEntity
    {
        public override string ToString() => $"{GetType().Name}; Id: {Id}";
    }

    /// <summary>
    /// Simulates MgStockTaking with exact property order from production.
    /// CRITICAL: Property order in code may differ from reflection order!
    /// </summary>
    public abstract class SimMgStockTaking<TStockTakingItem> : SimMgEntityBase 
        where TStockTakingItem : class
    {
        public DateTime StartDateTime { get; set; }
        public bool IsClosed { get; set; }
        public List<TStockTakingItem>? StockTakingItems { get; set; }
        public int Creator { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
    }

    /// <summary>
    /// Simulates the concrete StockTaking class.
    /// </summary>
    public class SimStockTaking : SimMgStockTaking<SimStockTakingItem>
    {
    }

    /// <summary>
    /// Simulates StockTakingItem with navigation properties.
    /// </summary>
    public class SimStockTakingItem : SimMgEntityBase
    {
        public int StockTakingId { get; set; }
        public int ProductId { get; set; }
        public bool IsMeasured { get; set; }
        public int OriginalStockQuantity { get; set; }
        public int MeasuredStockQuantity { get; set; }
        
        // Navigation property back to parent (circular!)
        public SimStockTaking? StockTaking { get; set; }
        
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
    }

    #endregion
}
