using AyCode.Core.Serializers.Binaries;

namespace AyCode.Core.Tests.TestModels;

public static class BenchmarkTestDataProvider
{
    public static List<TestDataSet> CreateTestDataSets(bool resetId = true)
    {
        return new List<TestDataSet>
        {
            CreateSmallTestData(resetId),
            CreateMediumTestData(resetId),
            CreateLargeTestData(resetId),
            CreateRepeatedStringsTestData(resetId),
            CreateDeepNestedTestData(resetId)
        };
    }

    public static TestOrder CreateProfilerOrder()
    {
        TestDataFactory.ResetIdCounter();
        var sharedTag = TestDataFactory.CreateTag("SharedTag");
        var sharedUser = TestDataFactory.CreateUser("shareduser");
        return TestDataFactory.CreateOrder(
            itemCount: 3,
            palletsPerItem: 3,
            measurementsPerPallet: 3,
            pointsPerMeasurement: 4,
            sharedTag: sharedTag,
            sharedUser: sharedUser);
    }

    private static TestDataSet CreateSmallTestData(bool resetId = true)
    {
        if (resetId) TestDataFactory.ResetIdCounter();

        var sharedTag = TestDataFactory.CreateTag("SharedTag");
        var sharedUser = TestDataFactory.CreateUser("shareduser");

        var order = TestDataFactory.CreateOrder(
            itemCount: 2,
            palletsPerItem: 2,
            measurementsPerPallet: 2,
            pointsPerMeasurement: 2,
            sharedTag: sharedTag,
            sharedUser: sharedUser);

        ClearDeepLevelRefs(order);

        return new TestDataSet("Small (2x2x2x2)", order, iidRefPercent: 20);
    }

    private static TestDataSet CreateMediumTestData(bool resetId = true)
    {
        if (resetId) TestDataFactory.ResetIdCounter();

        var sharedTag = TestDataFactory.CreateTag("SharedTag");
        var sharedUser = TestDataFactory.CreateUser("shareduser");
        var sharedMeta = TestDataFactory.CreateMetadata("shared", withChild: true);

        var sharedPreferences = new UserPreferences
        {
            Theme = "dark",
            Language = "en-US",
            NotificationsEnabled = true,
            EmailDigestFrequency = "weekly"
        };
        sharedUser.Preferences = sharedPreferences;

        var order = TestDataFactory.CreateOrder(
            itemCount: 3,
            palletsPerItem: 3,
            measurementsPerPallet: 3,
            pointsPerMeasurement: 4,
            sharedTag: sharedTag,
            sharedUser: sharedUser,
            sharedMetadata: sharedMeta,
            sharedPreferences: sharedPreferences);

        ClearDeepLevelRefs(order);

        return new TestDataSet("Medium (3x3x3x4)", order, iidRefPercent: 20);
    }

    private static TestDataSet CreateLargeTestData(bool resetId = true)
    {
        if (resetId) TestDataFactory.ResetIdCounter();

        var sharedTag = TestDataFactory.CreateTag("SharedTag");
        var sharedUser = TestDataFactory.CreateUser("shareduser");

        var sharedPreferences = new UserPreferences
        {
            Theme = "light",
            Language = "de-DE",
            NotificationsEnabled = false,
            EmailDigestFrequency = "daily"
        };
        sharedUser.Preferences = sharedPreferences;

        var order = TestDataFactory.CreateOrder(
            itemCount: 5,
            palletsPerItem: 5,
            measurementsPerPallet: 5,
            pointsPerMeasurement: 10,
            sharedTag: sharedTag,
            sharedUser: sharedUser,
            sharedPreferences: sharedPreferences);

        ClearDeepLevelRefs(order);

        return new TestDataSet("Large (5x5x5x10)", order, iidRefPercent: 20);
    }

    private static TestDataSet CreateRepeatedStringsTestData(bool resetId = true)
    {
        if (resetId) TestDataFactory.ResetIdCounter();

        var sharedTag = TestDataFactory.CreateTag("RepeatedTag");
        var sharedUser = TestDataFactory.CreateUser("repeateduser");

        var sharedPreferences = new UserPreferences
        {
            Theme = "dark",
            Language = "en-US",
            NotificationsEnabled = true,
            EmailDigestFrequency = "weekly"
        };
        sharedUser.Preferences = sharedPreferences;

        var order = TestDataFactory.CreateOrder(
            itemCount: 10,
            palletsPerItem: 2,
            measurementsPerPallet: 2,
            pointsPerMeasurement: 2,
            sharedTag: sharedTag,
            sharedUser: sharedUser,
            sharedPreferences: sharedPreferences);

        // Repeated string fields — ProductName on items + PalletCode on pallets. Both are common
        // across the hierarchy, exercising string-interning deduplication on the Default preset
        // (which has UseStringInterning = All). Targeting ~20% repeated-string share overall.
        // Strings contain non-ASCII characters (Hungarian accented letters → multi-byte UTF-8) so the
        // benchmark reflects real-world i18n payloads, not just the ASCII FixStr fast-path.
        foreach (var item in order.Items)
        {
            item.Status = TestStatus.Processing;
            item.ProductName = "TermékNév_IsmétlődőTesztAdat_árvíztűrőtükörfúrógép";

            foreach (var pallet in item.Pallets)
            {
                pallet.PalletCode = "RaklapKód_IsmétlődőTesztAdat_árvíztűrő";
            }
        }

        ClearDeepLevelRefs(order);

        return new TestDataSet("Repeated Strings (10 items)", order, iidRefPercent: 20);
    }

    private static TestDataSet CreateDeepNestedTestData(bool resetId = true)
    {
        if (resetId) TestDataFactory.ResetIdCounter();

        var sharedTag = TestDataFactory.CreateTag("DeepTag");
        var sharedUser = TestDataFactory.CreateUser("deepuser");
        var sharedCategory = TestDataFactory.CreateCategory("DeepCategory");

        var sharedPreferences = new UserPreferences
        {
            Theme = "light",
            Language = "fr-FR",
            NotificationsEnabled = false,
            EmailDigestFrequency = "monthly"
        };
        sharedUser.Preferences = sharedPreferences;

        var order = TestDataFactory.CreateOrder(
            itemCount: 2,
            palletsPerItem: 4,
            measurementsPerPallet: 4,
            pointsPerMeasurement: 8,
            sharedTag: sharedTag,
            sharedUser: sharedUser,
            sharedPreferences: sharedPreferences,
            sharedCategory: sharedCategory);

        ClearDeepLevelRefs(order);

        return new TestDataSet("Deep Nested (2x4x4x8)", order, iidRefPercent: 20);
    }

    private static void ClearDeepLevelRefs(TestOrder order)
    {
        // Keep shared IId refs at the pallet level (Tag + Inspector) — these contribute the bulk of
        // the ~20% IId-ref share that the test data targets. Only Category is cleared at this level
        // (one-of-three clears keep the share moderate). The deeper measurement / point levels are
        // cleared entirely so deep-tree ref noise does not skew the share upward beyond ~20%.
        foreach (var item in order.Items)
        {
            foreach (var pallet in item.Pallets)
            {
                // pallet.Tag = null;       // KEEP for ~20% IId-ref share (was cleared)
                // pallet.Inspector = null; // KEEP for ~20% IId-ref share (was cleared)
                pallet.Category = null;

                foreach (var measurement in pallet.Measurements)
                {
                    measurement.Tag = null;
                    measurement.Operator = null;

                    foreach (var point in measurement.Points)
                    {
                        point.Tag = null;
                        point.Verifier = null;
                    }
                }
            }
        }
    }
}

public sealed class TestDataSet
{
    public string Name { get; }
    public TestOrder Order { get; }

    /// <summary>
    /// Percentage of IId shared references in the data (0-100).
    /// Higher values mean more deduplication benefit for Default mode.
    /// </summary>
    public int IIdRefPercent { get; }

    public TestDataSet(string name, TestOrder order, int iidRefPercent = 0)
    {
        Name = name;
        Order = order;
        IIdRefPercent = iidRefPercent;
    }

    public string TypeName => Order.GetType().Name;

    /// <summary>
    /// Gets display name including IId ref percentage if set.
    /// </summary>
    public string DisplayName => IIdRefPercent > 0
        ? $"{Name} [{IIdRefPercent}% IId refs]"
        : Name;
}
