using AyCode.Core.Extensions;
using AyCode.Core.Interfaces;
using AyCode.Core.Serializers.Attributes;
using AyCode.Core.Serializers.Binaries;
using AyCode.Core.Serializers.Jsons;
using MemoryPack;
using MessagePack;
using MongoDB.Bson.Serialization.Attributes;
using Newtonsoft.Json;

namespace AyCode.Core.Tests.TestModels;

#region Shared Enums

/// <summary>
/// Common status enum for all test entities
/// </summary>
public enum TestStatus
{
    Pending = 5,
    Active = 10,
    Processing = 20,
    Completed = 30,
    Shipped = 40,
    OnHold = 50
}

/// <summary>
/// Priority levels for tasks and projects
/// </summary>
public enum TestPriority
{
    Low = 0,
    Medium = 1,
    High = 2,
    Critical = 3
}

/// <summary>
/// User roles for testing
/// </summary>
public enum TestUserRole
{
    User = 0,
    Manager = 1,
    Admin = 2
}

#endregion

#region Shared Reference Types (IId-based for $id/$ref testing)

/// <summary>
/// Shared tag/label - used across multiple entities for cross-reference testing.
/// Implements IId&lt;int&gt; for semantic $id/$ref serialization.
/// </summary>
[MemoryPackable]
[AcBinarySerializable(false)]
[MessagePackObject]
public partial class SharedTag : IId<int>
{
    [Key(0)]
    public int Id { get; set; }
    [Key(1)]
    public string Name { get; set; } = "";
    [AcStringIntern(true)]
    [Key(2)]
    public string Color { get; set; } = "#000000";
    [Key(3)]
    public int Priority { get; set; }
    [Key(4)]
    public bool IsActive { get; set; } = true;
    [Key(5)]
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    [Key(6)]
    public string? Description { get; set; }
}

/// <summary>
/// Shared category - for hierarchical cross-reference testing.
/// </summary>
[MemoryPackable]
[AcBinarySerializable(false)]
[MessagePackObject]
public partial class SharedCategory : IId<int>
{
    [Key(0)]
    public int Id { get; set; }
    [Key(1)]
    public string Name { get; set; } = "";
    [Key(2)]
    public string? Description { get; set; }
    [Key(3)]
    public int SortOrder { get; set; }
    [Key(4)]
    public bool IsDefault { get; set; }
    [Key(5)]
    public int? ParentCategoryId { get; set; }
    [Key(6)]
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    [Key(7)]
    public DateTime? UpdatedAt { get; set; }
}

/// <summary>
/// Shared user reference - appears in many places to test $ref deduplication.
/// </summary>
[MemoryPackable]
[AcBinarySerializable(false)]
[MessagePackObject]
public partial class SharedUser : IId<int>
{
    [Key(0)]
    public int Id { get; set; }
    [Key(1)]
    public string Username { get; set; } = "";
    [Key(2)]
    public string Email { get; set; } = "";
    [Key(3)]
    public string FirstName { get; set; } = "";
    [Key(4)]
    public string LastName { get; set; } = "";
    [Key(5)]
    public bool IsActive { get; set; } = true;
    [Key(6)]
    public TestUserRole Role { get; set; } = TestUserRole.User;
    [Key(7)]
    public DateTime? LastLoginAt { get; set; }
    [Key(8)]
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    [Key(9)]
    public UserPreferences? Preferences { get; set; }
}

/// <summary>
/// User preferences - non-IId nested object
/// </summary>
[MemoryPackable]
[AcBinarySerializable(false)]
[MessagePackObject]
public partial class UserPreferences
{
    [AcStringIntern(true)]
    [Key(0)]
    public string Theme { get; set; } = "light";
    [AcStringIntern(true)]
    [Key(1)]
    public string Language { get; set; } = "en-US";
    [Key(2)]
    public bool NotificationsEnabled { get; set; } = true;
    [AcStringIntern(true)]
    [Key(3)]
    public string? EmailDigestFrequency { get; set; }
}

#endregion

#region Non-IId Metadata (Newtonsoft numeric $id/$ref testing)

/// <summary>
/// Non-IId metadata class - uses Newtonsoft PreserveReferencesHandling (numeric $id/$ref).
/// Does NOT implement IId, so uses standard Newtonsoft reference tracking.
/// </summary>
[MemoryPackable]
[AcBinarySerializable(false)]
[MessagePackObject]
public partial class MetadataInfo
{
    [AcStringIntern(true)]
    [Key(0)]
    public string Key { get; set; } = "";
    [AcStringIntern(true)]
    [Key(1)]
    public string Value { get; set; } = "";
    [Key(2)]
    public DateTime Timestamp { get; set; } = DateTime.UtcNow;
    
    /// <summary>
    /// Nested metadata for deep Newtonsoft reference testing
    /// </summary>
    [Key(3)]
    public MetadataInfo? ChildMetadata { get; set; }
}

#endregion

#region 5-Level Test Hierarchy (Order -> Item -> Pallet -> Measurement -> Point)

/// <summary>
/// Level 1: Main order - root of the hierarchy
/// </summary>
[MemoryPackable]
[AcBinarySerializable(false)]
[MessagePackObject]
public partial class TestOrder : IId<int>
{
    [Key(0)]
    public int Id { get; set; }
    [Key(1)]
    public string OrderNumber { get; set; } = "";
    [Key(2)]
    public TestStatus Status { get; set; } = TestStatus.Pending;
    [Key(3)]
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    [Key(4)]
    public DateTime? PaidDateUtc { get; set; }
    [Key(5)]
    public decimal TotalAmount { get; set; }

    // Level 2 collection
    [Key(6)]
    public List<TestOrderItem> Items { get; set; } = [];

    // Shared reference properties (for $id/$ref testing)
    [Key(7)]
    public SharedTag? PrimaryTag { get; set; }
    [Key(8)]
    public SharedTag? SecondaryTag { get; set; }
    [Key(9)]
    public SharedUser? Owner { get; set; }
    [Key(10)]
    public SharedCategory? Category { get; set; }
    
    // Collection of shared references
    [Key(11)]
    public List<SharedTag> Tags { get; set; } = [];

    // Non-IId metadata (for Newtonsoft $ref testing)
    [Key(12)]
    public MetadataInfo? OrderMetadata { get; set; }
    [Key(13)]
    public MetadataInfo? AuditMetadata { get; set; }
    [Key(14)]
    public List<MetadataInfo> MetadataList { get; set; } = [];

    // NoMerge collection for testing replace behavior
    [JsonNoMergeCollection]
    [Key(15)]
    public List<TestOrderItem> NoMergeItems { get; set; } = [];

    // Parent reference - ignored by all serializers to prevent circular references
    [MemoryPackIgnore]
    [JsonIgnore]
    [IgnoreMember]
    [BsonIgnore]
    public object? Parent { get; set; }
}

/// <summary>
/// Level 1: Main order - root of the hierarchy
/// </summary>
[AcBinarySerializable(false)]
public partial class TestOrder_Circ_Ref : IId<int>
{
    public int Id { get; set; }
    public string OrderNumber { get; set; } = "";
    public TestStatus Status { get; set; } = TestStatus.Pending;
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    public DateTime? PaidDateUtc { get; set; }
    public decimal TotalAmount { get; set; }

    // Level 2 collection
    public List<TestOrderItem_Circ_Ref> Items { get; set; } = [];

    // Shared reference properties (for $id/$ref testing)
    public SharedTag? PrimaryTag { get; set; }
    public SharedTag? SecondaryTag { get; set; }
    public SharedUser? Owner { get; set; }
    public SharedCategory? Category { get; set; }
    
    // Collection of shared references
    public List<SharedTag> Tags { get; set; } = [];
    public MetadataInfo? OrderMetadata { get; set; }
    public MetadataInfo? AuditMetadata { get; set; }
    public List<MetadataInfo> MetadataList { get; set; } = [];

    // NoMerge collection for testing replace behavior
    [JsonNoMergeCollection]
    public List<TestOrderItem_Circ_Ref> NoMergeItems { get; set; } = [];

    public object? Parent { get; set; }
}
/// <summary>
/// Level 2: Order item with pallets
/// </summary>
[MemoryPackable]
[AcBinarySerializable(false)]
[MessagePackObject]
public partial class TestOrderItem : IId<int>
{
    [Key(0)]
    public int Id { get; set; }
    [AcStringIntern(true)]
    [Key(1)]
    public string ProductName { get; set; } = "";
    [Key(2)]
    public int Quantity { get; set; }
    [Key(3)]
    public decimal UnitPrice { get; set; }
    [Key(4)]
    public TestStatus Status { get; set; } = TestStatus.Pending;

    // Level 3 collection
    [Key(5)]
    public List<TestPallet> Pallets { get; set; } = [];

    // Shared references
    [Key(6)]
    public SharedTag? Tag { get; set; }
    [Key(7)]
    public SharedUser? Assignee { get; set; }
    [Key(8)]
    public MetadataInfo? ItemMetadata { get; set; }

    // Parent reference - ignored by all serializers to prevent circular references
    [MemoryPackIgnore]
    [JsonIgnore]
    [IgnoreMember]
    [BsonIgnore]
    public TestOrder? ParentOrder { get; set; }
}

/// <summary>
/// Level 2: Order item with pallets
/// </summary>
[AcBinarySerializable(false)]
public partial class TestOrderItem_Circ_Ref : IId<int>
{
    public int Id { get; set; }
    [AcStringIntern(true)]
    public string ProductName { get; set; } = "";
    public int Quantity { get; set; }
    public decimal UnitPrice { get; set; }
    public TestStatus Status { get; set; } = TestStatus.Pending;

    // Level 3 collection
    public List<TestPallet> Pallets { get; set; } = [];

    // Shared references
    public SharedTag? Tag { get; set; }
    public SharedUser? Assignee { get; set; }
    public MetadataInfo? ItemMetadata { get; set; }

    public TestOrder_Circ_Ref? ParentOrder { get; set; }
}
/// <summary>
/// Level 3: Pallet containing measurements
/// </summary>
[MemoryPackable]
[AcBinarySerializable(false)]
[MessagePackObject]
public partial class TestPallet : IId<int>
{
    [Key(0)]
    public int Id { get; set; }
    [Key(1)]
    public string PalletCode { get; set; } = "";
    [Key(2)]
    public int TrayCount { get; set; }
    [Key(3)]
    public TestStatus Status { get; set; } = TestStatus.Pending;
    [Key(4)]
    public double Weight { get; set; }

    // Level 4 collection
    [Key(5)]
    public List<TestMeasurement> Measurements { get; set; } = [];

    // Shared IId references for better reference testing
    [Key(6)]
    public SharedTag? Tag { get; set; }
    [Key(7)]
    public SharedUser? Inspector { get; set; }
    [Key(8)]
    public SharedCategory? Category { get; set; }
    
    // Non-IId shared references
    [Key(9)]
    public MetadataInfo? PalletMetadata { get; set; }

    // Parent reference - ignored by all serializers to prevent circular references
    [MemoryPackIgnore]
    [JsonIgnore]
    [IgnoreMember]
    [BsonIgnore]
    public TestOrderItem? ParentItem { get; set; }
}

/// <summary>
/// Level 4: Measurement with multiple points
/// </summary>
[MemoryPackable]
[AcBinarySerializable(false)]
[MessagePackObject]
public partial class TestMeasurement : IId<int>
{
    [Key(0)]
    public int Id { get; set; }
    [Key(1)]
    public string Name { get; set; } = "";
    [Key(2)]
    public double TotalWeight { get; set; }
    [Key(3)]
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;

    // Level 5 collection
    [Key(4)]
    public List<TestMeasurementPoint> Points { get; set; } = [];

    // Shared IId references for better reference testing
    [Key(5)]
    public SharedTag? Tag { get; set; }
    [Key(6)]
    public SharedUser? Operator { get; set; }

    // Parent reference - ignored by all serializers to prevent circular references
    [MemoryPackIgnore]
    [JsonIgnore]
    [IgnoreMember]
    [BsonIgnore]
    public TestPallet? ParentPallet { get; set; }
}

/// <summary>
/// Level 5: Deepest level - measurement point
/// </summary>
[MemoryPackable]
[AcBinarySerializable(false)]
[MessagePackObject]
public partial class TestMeasurementPoint : IId<int>
{
    [Key(0)]
    public int Id { get; set; }
    [Key(1)]
    public string Label { get; set; } = "";
    [Key(2)]
    public double Value { get; set; }
    [Key(3)]
    public DateTime MeasuredAt { get; set; } = DateTime.UtcNow;

    // Shared IId reference for better reference testing (many points share same tag/user)
    [Key(4)]
    public SharedTag? Tag { get; set; }
    [Key(5)]
    public SharedUser? Verifier { get; set; }

    // Parent reference - ignored by all serializers to prevent circular references
    [MemoryPackIgnore]
    [JsonIgnore]
    [IgnoreMember]
    [BsonIgnore]
    public TestMeasurement? ParentMeasurement { get; set; }
}

#endregion

#region Guid-based IId types

/// <summary>
/// Order with Guid Id - for testing Guid-based IId
/// </summary>
[AcBinarySerializable(false)]
public class TestGuidOrder : IId<Guid>
{
    public Guid Id { get; set; }
    public string Code { get; set; } = "";
    public List<TestGuidItem> Items { get; set; } = [];
    public int Count { get; set; }
}

/// <summary>
/// Item with Guid Id
/// </summary>
[AcBinarySerializable(false)]
public class TestGuidItem : IId<Guid>
{
    public Guid Id { get; set; }
    public string Name { get; set; } = "";
    public int Qty { get; set; }
}

#endregion

#region Test-specific classes

/// <summary>
/// Simulates NopCommerce GenericAttribute - stores key-value pairs where DateTime values
/// are stored as strings in the database.
/// </summary>
[AcBinarySerializable(false)]
public class TestGenericAttribute
{
    public int Id { get; set; }
    public string Key { get; set; } = "";
    public string Value { get; set; } = "";
}

/// <summary>
/// DTO with GenericAttributes collection - simulates OrderDto with string-stored DateTime values.
/// This reproduces the production bug where Binary serialization was thought to corrupt DateTime strings.
/// </summary>
[AcBinarySerializable(false)]
public class TestDtoWithGenericAttributes : IId<int>
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public List<TestGenericAttribute> GenericAttributes { get; set; } = [];
}

/// <summary>
/// Order with nullable collections for null vs empty testing
/// </summary>
[AcBinarySerializable(false)]
public class TestOrderWithNullableCollections
{
    public int Id { get; set; }
    public string OrderNumber { get; set; } = "";
    public List<TestOrderItem>? Items { get; set; }
    public List<string>? Tags { get; set; }
}

/// <summary>
/// Class with all primitive types for WASM/serialization testing
/// </summary>
[MemoryPackable]
[AcBinarySerializable(false)]
public partial class PrimitiveTestClass
{
    public int IntValue { get; set; }
    public long LongValue { get; set; }
    public double DoubleValue { get; set; }
    public decimal DecimalValue { get; set; }
    public float FloatValue { get; set; }
    public bool BoolValue { get; set; }
    public string StringValue { get; set; } = "";
    public Guid GuidValue { get; set; }
    public DateTime DateTimeValue { get; set; }
    public TestStatus EnumValue { get; set; }
    public byte ByteValue { get; set; }
    public short ShortValue { get; set; }
    public int? NullableInt { get; set; }
    public int? NullableIntNull { get; set; }
}

/// <summary>
/// Class with extended primitive types for full serializer coverage.
/// Includes DateTimeOffset, TimeSpan, Dictionary, null properties.
/// </summary>
[AcBinarySerializable(false)]
public class ExtendedPrimitiveTestClass
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    
    // Extended primitive types not covered in PrimitiveTestClass
    public DateTimeOffset DateTimeOffsetValue { get; set; }
    public TimeSpan TimeSpanValue { get; set; }
    public uint UIntValue { get; set; }
    public ulong ULongValue { get; set; }
    public ushort UShortValue { get; set; }
    public sbyte SByteValue { get; set; }
    public char CharValue { get; set; }
    
    // Dictionary property for WriteDictionary coverage in object context
    public Dictionary<string, int>? Counts { get; set; }
    public Dictionary<string, string>? Labels { get; set; }
    
    // Nullable properties that will be null
    public string? NullString { get; set; }
    public TestOrderItem? NullObject { get; set; }
    
    // Nested object for complex serialization
    public SharedTag? Tag { get; set; }
}

/// <summary>
/// Class with array of objects containing null items for WriteNull coverage
/// </summary>
[AcBinarySerializable(false)]
public class ObjectWithNullItems
{
    public int Id { get; set; }
    public List<object?> MixedItems { get; set; } = [];
}

#endregion

#region Property Mismatch DTOs (Server has more properties than Client)

/// <summary>
/// "Server-side" DTO with extra properties that the "client" doesn't know about.
/// Used to test SkipValue functionality when deserializing unknown properties.
/// </summary>
[AcBinarySerializable(false)]
public class ServerCustomerDto : IId<int>
{
    public int Id { get; set; }
    
    // Common properties (both server and client have these)
    public string FirstName { get; set; } = "";
    public string LastName { get; set; } = "";
    
    // Extra properties (only server has these - client will skip them)
    public string Email { get; set; } = "";
    public string Phone { get; set; } = "";
    public string Address { get; set; } = "";
    public string City { get; set; } = "";
    public string Country { get; set; } = "";
    public string PostalCode { get; set; } = "";
    public string Company { get; set; } = "";
    public string Department { get; set; } = "";
    public string Notes { get; set; } = "";
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    public DateTime? LastLoginAt { get; set; }
    public TestStatus Status { get; set; } = TestStatus.Active;
    public bool IsVerified { get; set; }
    public int LoginCount { get; set; }
    public decimal Balance { get; set; }
}

/// <summary>
/// "Client-side" DTO with fewer properties than the server version.
/// When deserializing ServerCustomerDto data into this type, 
/// the deserializer must skip unknown properties correctly 
/// while still maintaining string intern table consistency.
/// </summary>
[AcBinarySerializable(false)]
public class ClientCustomerDto : IId<int>
{
    public int Id { get; set; }
    
    // Only basic properties - client doesn't need all server fields
    public string FirstName { get; set; } = "";
    public string LastName { get; set; } = "";
}

/// <summary>
/// Server DTO with nested objects that client doesn't know about.
/// Tests skipping complex nested structures.
/// </summary>
[AcBinarySerializable(false)]
public class ServerOrderWithExtras : IId<int>
{
    public int Id { get; set; }
    public string OrderNumber { get; set; } = "";
    public decimal TotalAmount { get; set; }
    
    // Nested object that client doesn't have
    public ServerCustomerDto? Customer { get; set; }
    
    // List of objects that client doesn't know about
    public List<ServerCustomerDto> RelatedCustomers { get; set; } = [];
    
    // Extra simple properties
    public string InternalNotes { get; set; } = "";
    public string ProcessingCode { get; set; } = "";
}

/// <summary>
/// Client version of the order - doesn't have Customer/RelatedCustomers properties.
/// </summary>
[AcBinarySerializable(false)]
public class ClientOrderSimple : IId<int>
{
    public int Id { get; set; }
    public string OrderNumber { get; set; } = "";
    public decimal TotalAmount { get; set; }
}

#endregion
