namespace AyCode.Core.Tests.TestModels;

/// <summary>
/// Generic factory for the 5-level test-data hierarchy (Order → OrderItem → Pallet → Measurement →
/// MeasurementPoint) + cross-cutting shared types (Tag, Category, User, Metadata, UserPreferences).
/// One closing-generic alias per family — see <see cref="TestDataFactory"/> (the <c>_All_True</c>
/// family, kept on the bare class name for MSTEST backward compatibility) and
/// <see cref="TestDataFactory_All_False"/> (the <c>_All_False</c> family).
///
/// <para>The static <c>_idCounter</c> below is per-closed-generic (verified via C# smoke): each family
/// has an independent ID sequence, so calls like <c>TestDataFactory.ResetIdCounter()</c> reset only the
/// <c>_All_True</c> counter, leaving any <c>TestDataFactory_All_False.NextId()</c> sequence intact.
/// Each family's "Reset → Next" pattern stays internally consistent.</para>
///
/// <para>All placeholder strings use Hungarian (UTF-8 multi-byte) content to exercise the UTF-8
/// encoder/decoder path rather than the ASCII fast-path. This makes the benchmark reflect realistic
/// i18n payloads, not just the FixStrAscii / StringAscii marker fast-paths.</para>
/// </summary>
public abstract class TestDataFactory<TOrder, TItem, TPallet, TMeasurement, TPoint, TTag, TUser, TCategory, TMetadata, TPreferences>
    where TOrder : TestOrderBase<TItem, TTag, TUser, TCategory, TMetadata, TPreferences>, new()
    where TItem : TestOrderItemBase<TPallet, TTag, TUser, TMetadata, TOrder, TPreferences>, new()
    where TPallet : TestPalletBase<TMeasurement, TTag, TUser, TCategory, TMetadata, TItem, TPreferences>, new()
    where TMeasurement : TestMeasurementBase<TPoint, TTag, TUser, TPallet, TPreferences>, new()
    where TPoint : TestMeasurementPointBase<TTag, TUser, TMeasurement, TPreferences>, new()
    where TTag : SharedTagBase, new()
    where TUser : SharedUserBase<TPreferences>, new()
    where TCategory : SharedCategoryBase, new()
    where TMetadata : MetadataInfoBase<TMetadata>, new()
    where TPreferences : UserPreferencesBase, new()
{
    private static int _idCounter = 1;

    /// <summary>
    /// Reset the ID counter (call in test setup). Resets ONLY this family's counter — sibling families
    /// keep their own independent counter state.
    /// </summary>
    public static void ResetIdCounter() => _idCounter = 1;

    /// <summary>
    /// Get the next unique ID. Per-family counter — see class docs for the isolation rationale.
    /// </summary>
    public static int NextId() => _idCounter++;

    #region Simple Object Creation

    /// <summary>
    /// Create a shared tag for cross-reference testing
    /// </summary>
    public static TTag CreateTag(string? name = null, string? color = null)
    {
        var id = _idCounter++;
        return new TTag
        {
            Id = id,
            Name = name ?? $"Tag-{id}",
            Color = color ?? $"Color-#{id:X2}{(id * 10) % 256:X2}{(id * 20) % 256:X2}",
            Priority = id % 5,
            IsActive = id % 2 == 0,
            CreatedAt = DateTime.UtcNow.AddDays(-id),
            Description = $"Tag description {id}"
        };
    }

    /// <summary>
    /// Create a shared category
    /// </summary>
    public static TCategory CreateCategory(string? name = null, int? parentId = null)
    {
        var id = _idCounter++;
        return new TCategory
        {
            Id = id,
            Name = name ?? $"Category-{id}",
            Description = $"Category description {id}",
            SortOrder = id * 100,
            IsDefault = id == 1,
            ParentCategoryId = parentId,
            CreatedAt = DateTime.UtcNow.AddMonths(-id),
            UpdatedAt = DateTime.UtcNow.AddDays(-id)
        };
    }

    /// <summary>
    /// Create a shared user for cross-reference testing
    /// </summary>
    public static TUser CreateUser(string? username = null, TestUserRole role = TestUserRole.User)
    {
        var id = _idCounter++;
        return new TUser
        {
            Id = id,
            Username = username ?? $"user{id}",
            Email = $"user{id}@test.com",
            FirstName = $"FirstName{id}",
            LastName = $"LastName{id}",
            IsActive = true,
            Role = role,
            LastLoginAt = DateTime.UtcNow.AddHours(-id),
            CreatedAt = DateTime.UtcNow.AddYears(-1),
            Preferences = new TPreferences
            {
                Theme = id % 2 == 0 ? "dark" : "light",
                Language = "english",
                NotificationsEnabled = true,
                EmailDigestFrequency = "daily"
            }
        };
    }

    /// <summary>
    /// Create metadata info (non-IId)
    /// </summary>
    public static TMetadata CreateMetadata(string? key = null, bool withChild = false)
    {
        var id = _idCounter++;
        return new TMetadata
        {
            Key = key ?? $"Metadata-{id}",
            Value = $"MetadataValue-{id}",
            Timestamp = DateTime.UtcNow.AddMinutes(-id * 10),
            ChildMetadata = withChild ? CreateMetadata($"Child-{id}", false) : null
        };
    }

    #endregion

    #region Hierarchy Creation (5 Levels)

    /// <summary>
    /// Create a deep order hierarchy with configurable depth.
    /// Supports both IId-based (Tag, User, Category) and Non-IId (Preferences) shared references.
    /// </summary>
    public static TOrder CreateOrder(
        int itemCount = 2,
        int palletsPerItem = 2,
        int measurementsPerPallet = 2,
        int pointsPerMeasurement = 3,
        TTag? sharedTag = null,
        TUser? sharedUser = null,
        TMetadata? sharedMetadata = null,
        TPreferences? sharedPreferences = null,
        TCategory? sharedCategory = null)
    {
        // If sharedUser is provided but no sharedPreferences, use the user's preferences as shared
        sharedPreferences ??= sharedUser?.Preferences;

        var order = new TOrder
        {
            Id = _idCounter++,
            OrderNumber = $"Order-{_idCounter:D4}",
            Status = TestStatus.Pending,
            CreatedAt = DateTime.UtcNow,
            TotalAmount = 1000m + _idCounter * 100,
            PrimaryTag = sharedTag,
            SecondaryTag = sharedTag, // Same reference for $ref testing
            Owner = sharedUser,
            Category = sharedCategory,
            OrderMetadata = sharedMetadata,
            AuditMetadata = sharedMetadata // Same reference for Newtonsoft $ref
        };

        if (sharedTag != null)
        {
            order.Tags.Add(sharedTag);
        }

        for (int i = 0; i < itemCount; i++)
        {
            var item = CreateOrderItem(
                palletsPerItem,
                measurementsPerPallet,
                pointsPerMeasurement,
                sharedTag,
                sharedUser,
                sharedMetadata,
                sharedPreferences,
                sharedCategory);
            item.ParentOrder = order;
            order.Items.Add(item);
        }

        return order;
    }

    /// <summary>
    /// Create an order item with pallets.
    /// </summary>
    public static TItem CreateOrderItem(
        int palletCount = 2,
        int measurementsPerPallet = 2,
        int pointsPerMeasurement = 3,
        TTag? sharedTag = null,
        TUser? sharedUser = null,
        TMetadata? sharedMetadata = null,
        TPreferences? sharedPreferences = null,
        TCategory? sharedCategory = null)
    {
        // Create assignee - if sharedUser provided, use it. Otherwise create new user with sharedPreferences
        TUser? assignee = sharedUser;
        if (assignee == null && sharedPreferences != null)
        {
            // Create a new user but with shared preferences (Non-IId ref testing)
            assignee = CreateUser();
            assignee.Preferences = sharedPreferences;
        }

        var item = new TItem
        {
            Id = _idCounter++,
            ProductName = $"Product-{_idCounter}",
            Quantity = 10 + _idCounter,
            UnitPrice = 5.5m * _idCounter,
            Status = TestStatus.Pending,
            Tag = sharedTag,
            Assignee = assignee,
            ItemMetadata = sharedMetadata
        };

        for (int i = 0; i < palletCount; i++)
        {
            // Pass shared references to all levels - creates many shared refs!
            var pallet = CreatePallet(
                measurementsPerPallet,
                pointsPerMeasurement,
                sharedMetadata,
                sharedTag,        // IId shared ref
                sharedUser,       // IId shared ref
                sharedCategory);  // IId shared ref
            pallet.ParentItem = item;
            item.Pallets.Add(pallet);
        }

        return item;
    }

    /// <summary>
    /// Create a pallet with measurements
    /// </summary>
    public static TPallet CreatePallet(
        int measurementCount = 2,
        int pointsPerMeasurement = 3,
        TMetadata? sharedMetadata = null,
        TTag? sharedTag = null,
        TUser? sharedInspector = null,
        TCategory? sharedCategory = null)
    {
        var pallet = new TPallet
        {
            Id = _idCounter++,
            PalletCode = $"PalletCode-{_idCounter:D4}",
            TrayCount = 5 + _idCounter % 10,
            Status = TestStatus.Pending,
            Weight = 100.5 + _idCounter,
            PalletMetadata = sharedMetadata,
            Tag = sharedTag,
            Inspector = sharedInspector,
            Category = sharedCategory
        };

        for (int i = 0; i < measurementCount; i++)
        {
            var measurement = CreateMeasurement(pointsPerMeasurement, sharedTag, sharedInspector);
            measurement.ParentPallet = pallet;
            pallet.Measurements.Add(measurement);
        }

        return pallet;
    }

    /// <summary>
    /// Create a measurement with points
    /// </summary>
    public static TMeasurement CreateMeasurement(
        int pointCount = 3,
        TTag? sharedTag = null,
        TUser? sharedOperator = null)
    {
        var measurement = new TMeasurement
        {
            Id = _idCounter++,
            Name = $"Measurement-{_idCounter}",
            TotalWeight = 100.5 + _idCounter,
            CreatedAt = DateTime.UtcNow,
            Tag = sharedTag,
            Operator = sharedOperator
        };

        for (int i = 0; i < pointCount; i++)
        {
            var point = CreateMeasurementPoint(sharedTag, sharedOperator);
            point.ParentMeasurement = measurement;
            measurement.Points.Add(point);
        }

        return measurement;
    }

    /// <summary>
    /// Create a measurement point
    /// </summary>
    public static TPoint CreateMeasurementPoint(
        TTag? sharedTag = null,
        TUser? sharedVerifier = null)
    {
        var id = _idCounter++;
        return new TPoint
        {
            Id = id,
            Label = $"MeasurePoint-{id}",
            Value = 10.5 + (id * 0.1),
            MeasuredAt = DateTime.UtcNow,
            Tag = sharedTag,
            Verifier = sharedVerifier
        };
    }

    #endregion
}

// ============================================================================================
// Closing-generic aliases. Each family carries its own static <c>_idCounter</c> (per-closed-generic
// isolation — see C# runtime semantics). The base-class generic methods are accessible through both
// aliases unchanged.
// ============================================================================================

/// <summary>
/// <c>_All_True</c> family factory — preserves the bare-name API surface
/// (<c>TestDataFactory.CreateTag(...)</c>, etc.) that the MSTEST tests and benchmark consumers depend
/// on. Adds family-specific extras (<see cref="CreateBenchmarkOrder"/>, <see cref="CreateLargeScaleBenchmarkOrder"/>,
/// <see cref="CreatePrimitiveTestData"/>) that the generic base intentionally doesn't carry.
/// </summary>
public sealed class TestDataFactory : TestDataFactory<
    TestOrder_All_True, TestOrderItem_All_True, TestPallet_All_True, TestMeasurement_All_True, TestMeasurementPoint_All_True,
    SharedTag_All_True, SharedUser_All_True, SharedCategory_All_True, MetadataInfo_All_True, UserPreferences_All_True>
{
    /// <summary>
    /// Create a large graph for benchmarking with many cross-references.
    /// Creates approximately (itemCount * palletsPerItem * measurementsPerPallet * pointsPerMeasurement) objects.
    /// </summary>
    public static TestOrder_All_True CreateBenchmarkOrder(
        int itemCount = 5,
        int palletsPerItem = 4,
        int measurementsPerPallet = 3,
        int pointsPerMeasurement = 5)
    {
        ResetIdCounter();

        // Create shared references that will be used throughout
        var sharedTags = Enumerable.Range(1, 10).Select(_ => CreateTag()).ToList();
        var sharedUser = CreateUser("mérőfelhasználó", TestUserRole.Admin);
        var sharedMetadata = CreateMetadata("mérőteszt", withChild: true);

        var order = new TestOrder_All_True
        {
            Id = NextId(),
            OrderNumber = $"MÉRŐTESZT-{NextId():D6}",
            Status = TestStatus.Processing,
            CreatedAt = DateTime.UtcNow,
            TotalAmount = 999999.99m,
            PrimaryTag = sharedTags[0],
            SecondaryTag = sharedTags[0],
            Owner = sharedUser,
            Category = CreateCategory("Mérőteszt"),
            OrderMetadata = sharedMetadata,
            AuditMetadata = sharedMetadata,
            Tags = sharedTags.Take(3).ToList()
        };

        for (int i = 0; i < itemCount; i++)
        {
            var item = new TestOrderItem_All_True
            {
                Id = NextId(),
                ProductName = $"MérőTermék-{i}",
                Quantity = 100 + i * 10,
                UnitPrice = 25.99m + i,
                Status = (TestStatus)(i % 5),
                Tag = sharedTags[i % sharedTags.Count],
                Assignee = sharedUser,
                ItemMetadata = sharedMetadata
            };
            item.ParentOrder = order;

            for (int p = 0; p < palletsPerItem; p++)
            {
                var pallet = new TestPallet_All_True
                {
                    Id = NextId(),
                    PalletCode = $"Raklapkód-{i}-{p}",
                    TrayCount = 10 + p,
                    Status = (TestStatus)(p % 4),
                    Weight = 500.0 + p * 50,
                    PalletMetadata = sharedMetadata
                };
                pallet.ParentItem = item;

                for (int m = 0; m < measurementsPerPallet; m++)
                {
                    var measurement = new TestMeasurement_All_True
                    {
                        Id = NextId(),
                        Name = $"Mérés-{i}-{p}-{m}",
                        TotalWeight = 50.0 + m * 10,
                        CreatedAt = DateTime.UtcNow.AddMinutes(-m)
                    };
                    measurement.ParentPallet = pallet;

                    for (int pt = 0; pt < pointsPerMeasurement; pt++)
                    {
                        var point = new TestMeasurementPoint_All_True
                        {
                            Id = NextId(),
                            Label = $"MérőPnt-{i}-{p}-{m}-{pt}",
                            Value = 1.0 + pt * 0.5,
                            MeasuredAt = DateTime.UtcNow.AddSeconds(-pt)
                        };
                        point.ParentMeasurement = measurement;
                        measurement.Points.Add(point);
                    }
                    pallet.Measurements.Add(measurement);
                }
                item.Pallets.Add(pallet);
            }
            order.Items.Add(item);
        }

        return order;
    }

    /// <summary>
    /// Create a large-scale benchmark order similar to production workloads.
    /// Targets ~50,000-100,000+ IId objects with deep hierarchy and shared references.
    /// </summary>
    public static TestOrder_All_True CreateLargeScaleBenchmarkOrder(
        int rootItemCount = 500,
        int palletsPerItem = 3,
        int measurementsPerPallet = 3,
        int pointsPerMeasurement = 4)
    {
        ResetIdCounter();

        // Create shared references - these will be heavily reused (tests $ref handling)
        var sharedTags = Enumerable.Range(1, 50).Select(_ => CreateTag()).ToList();
        var sharedUsers = Enumerable.Range(1, 20).Select(i => CreateUser($"felhasználó{i}", (TestUserRole)(i % 4))).ToList();
        var sharedMetadata = CreateMetadata("nagy-méretű", withChild: true);
        var sharedCategories = Enumerable.Range(1, 10).Select(i => CreateCategory($"Kategória-{i}")).ToList();

        var order = new TestOrder_All_True
        {
            Id = NextId(),
            OrderNumber = $"NAGYMÉRET-{NextId():D8}",
            Status = TestStatus.Processing,
            CreatedAt = DateTime.UtcNow,
            TotalAmount = 9999999.99m,
            PrimaryTag = sharedTags[0],
            SecondaryTag = sharedTags[0], // Same ref
            Owner = sharedUsers[0],
            Category = sharedCategories[0],
            OrderMetadata = sharedMetadata,
            AuditMetadata = sharedMetadata, // Same ref
            Tags = sharedTags.Take(5).ToList()
        };

        for (int i = 0; i < rootItemCount; i++)
        {
            var item = new TestOrderItem_All_True
            {
                Id = NextId(),
                ProductName = $"Termék-{i}",
                Quantity = 100 + i,
                UnitPrice = 10.99m + (i % 100),
                Status = (TestStatus)(i % 5),
                Tag = sharedTags[i % sharedTags.Count], // Shared ref
                Assignee = sharedUsers[i % sharedUsers.Count], // Shared ref
                ItemMetadata = sharedMetadata // Shared ref
            };
            item.ParentOrder = order;

            for (int p = 0; p < palletsPerItem; p++)
            {
                var pallet = new TestPallet_All_True
                {
                    Id = NextId(),
                    PalletCode = $"Raklapkód-{i}-{p}",
                    TrayCount = 5 + (p % 10),
                    Status = (TestStatus)(p % 4),
                    Weight = 100.0 + p * 10,
                    PalletMetadata = sharedMetadata // Shared ref
                };
                pallet.ParentItem = item;

                for (int m = 0; m < measurementsPerPallet; m++)
                {
                    var measurement = new TestMeasurement_All_True
                    {
                        Id = NextId(),
                        Name = $"Mérés-{i}-{p}-{m}",
                        TotalWeight = 10.0 + m,
                        CreatedAt = DateTime.UtcNow
                    };
                    measurement.ParentPallet = pallet;

                    for (int pt = 0; pt < pointsPerMeasurement; pt++)
                    {
                        var point = new TestMeasurementPoint_All_True
                        {
                            Id = NextId(),
                            Label = $"MérőPnt-{i}-{p}-{m}-{pt}",
                            Value = pt * 0.1,
                            MeasuredAt = DateTime.UtcNow
                        };
                        point.ParentMeasurement = measurement;
                        measurement.Points.Add(point);
                    }
                    pallet.Measurements.Add(measurement);
                }
                item.Pallets.Add(pallet);
            }
            order.Items.Add(item);
        }

        return order;
    }

    /// <summary>
    /// Calculate approximate object count for large-scale benchmark.
    /// </summary>
    public static int CalculateObjectCount(int rootItems, int pallets, int measurements, int points)
    {
        // 1 order + rootItems + (rootItems * pallets) + (rootItems * pallets * measurements) + (rootItems * pallets * measurements * points)
        // Plus shared objects (tags, users, metadata, categories)
        var sharedObjects = 50 + 20 + 2 + 10; // tags + users + metadata + categories
        var hierarchyObjects = 1 + rootItems + (rootItems * pallets) + (rootItems * pallets * measurements) + (rootItems * pallets * measurements * points);
        return sharedObjects + hierarchyObjects;
    }

    /// <summary>
    /// Create primitive test data for all-types testing
    /// </summary>
    public static PrimitiveTestClass CreatePrimitiveTestData()
    {
        return new PrimitiveTestClass
        {
            IntValue = int.MaxValue,
            LongValue = long.MaxValue,
            DoubleValue = 3.14159265358979,
            DecimalValue = 12345.6789m,
            FloatValue = 1.5f,
            BoolValue = true,
            StringValue = "Teszt Szöveg árvíztűrőtükörfúrógép",
            GuidValue = Guid.Parse("12345678-1234-1234-1234-123456789abc"),
            DateTimeValue = new DateTime(2024, 12, 25, 12, 30, 45, DateTimeKind.Utc),
            EnumValue = TestStatus.Shipped,
            ByteValue = 255,
            ShortValue = short.MaxValue,
            NullableInt = 42,
            NullableIntNull = null
        };
    }
}

/// <summary>
/// <c>_All_False</c> family factory — benchmark Phase 1 target. The generic-base factory methods
/// produce <c>_All_False</c>-typed graphs via the closed-generic <c>new T()</c> calls. No
/// family-specific extras here (the legacy <see cref="TestDataFactory.CreateBenchmarkOrder"/> etc.
/// stay on the <c>_All_True</c> alias because their existing consumers are <c>_All_True</c>-tied).
/// </summary>
public sealed class TestDataFactory_All_False : TestDataFactory<
    TestOrder_All_False, TestOrderItem_All_False, TestPallet_All_False, TestMeasurement_All_False, TestMeasurementPoint_All_False,
    SharedTag_All_False, SharedUser_All_False, SharedCategory_All_False, MetadataInfo_All_False, UserPreferences_All_False>
{
}
