using AyCode.Core.Extensions;
using AyCode.Core.Serializers.Binaries;
using FruitBank.Common.Entities;
using System.Reflection;

namespace FruitBankHybrid.Shared.Tests;

[TestClass]
public class StockTakingSerializerTests
{
    [TestMethod]
    public void StockTaking_WithNullItems_RoundTrip()
    {
        var stockTaking = new StockTaking
        {
            Id = 1,
            StartDateTime = new DateTime(2025, 1, 24, 10, 0, 0, DateTimeKind.Utc),
            IsClosed = false,
            Creator = 6,
            Created = new DateTime(2025, 1, 24, 15, 25, 0, DateTimeKind.Utc),
            Modified = new DateTime(2025, 1, 24, 16, 0, 0, DateTimeKind.Utc),
            StockTakingItems = null
        };

        // Log StockTaking properties
        Console.WriteLine("=== StockTaking Properties ===");
        var stProps = typeof(StockTaking).GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(p => p.CanRead && p.GetIndexParameters().Length == 0)
            .ToArray();
        for (int i = 0; i < stProps.Length; i++)
            Console.WriteLine($"  [{i}] {stProps[i].Name} : {stProps[i].PropertyType.Name} (from: {stProps[i].DeclaringType?.Name})");

        // Log StockTakingItem properties
        Console.WriteLine("\n=== StockTakingItem Properties ===");
        var stiProps = typeof(StockTakingItem).GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(p => p.CanRead && p.GetIndexParameters().Length == 0)
            .ToArray();
        for (int i = 0; i < stiProps.Length; i++)
        {
            var hasIgnore = Attribute.IsDefined(stiProps[i], typeof(Newtonsoft.Json.JsonIgnoreAttribute)) ||
                            Attribute.IsDefined(stiProps[i], typeof(System.Text.Json.Serialization.JsonIgnoreAttribute));
            Console.WriteLine($"  [{i}] {stiProps[i].Name} : {stiProps[i].PropertyType.Name}{(hasIgnore ? " [IGNORED]" : "")}");
        }

        var binary = stockTaking.ToBinary();
        Console.WriteLine($"\nBinary length: {binary.Length}");

        // Parse header
        var pos = 0;
        var version = binary[pos++];
        var marker = binary[pos++];
        Console.WriteLine($"Version: {version}, Marker: 0x{marker:X2}");

        if ((marker & 0x10) != 0)
        {
            var propCount = binary[pos++];
            Console.WriteLine($"\n=== HEADER ({propCount} properties) ===");
            for (int i = 0; i < propCount; i++)
            {
                var strLen = binary[pos++];
                var propName = System.Text.Encoding.UTF8.GetString(binary, pos, strLen);
                pos += strLen;
                Console.WriteLine($"  [{i}]: '{propName}'");
            }
        }

        // Deserialize
        try
        {
            var result = binary.BinaryTo<StockTaking>();

            Console.WriteLine($"\n=== RESULT ===");
            Console.WriteLine($"Id: {result?.Id}");
            Console.WriteLine($"Creator: {result?.Creator}");
            Console.WriteLine($"Created: {result?.Created}");

            Assert.IsNotNull(result);
            Assert.AreEqual(1, result.Id, "Id mismatch");
            Assert.AreEqual(6, result.Creator, $"Creator should be 6, got {result.Creator}");
            Assert.AreEqual(stockTaking.Created, result.Created, $"Created mismatch");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"\n=== ERROR ===");
            Console.WriteLine(ex.Message);
            throw;
        }
    }

    [TestMethod]
    public void ListOfStockTaking_WithNullItems_RoundTrip()
    {
        var stockTakings = new List<StockTaking>
        {
            new() { Id = 1, StartDateTime = DateTime.UtcNow, IsClosed = false, Creator = 6, Created = DateTime.UtcNow, Modified = DateTime.UtcNow, StockTakingItems = null },
            new() { Id = 2, StartDateTime = DateTime.UtcNow, IsClosed = true, Creator = 12, Created = DateTime.UtcNow, Modified = DateTime.UtcNow, StockTakingItems = null }
        };

        var binary = stockTakings.ToBinary();
        Console.WriteLine($"Binary length: {binary.Length}");

        var result = binary.BinaryTo<List<StockTaking>>();

        Assert.IsNotNull(result);
        Assert.AreEqual(2, result.Count);
        Assert.AreEqual(6, result[0].Creator, $"First Creator should be 6, got {result[0].Creator}");
        Assert.AreEqual(12, result[1].Creator, $"Second Creator should be 12, got {result[1].Creator}");
    }

    [TestMethod]
    public void StockTaking_DebugBodyProperties()
    {
        var stockTaking = new StockTaking
        {
            Id = 1,
            StartDateTime = new DateTime(2025, 1, 24, 10, 0, 0, DateTimeKind.Utc),
            IsClosed = false,
            Creator = 6,
            Created = new DateTime(2025, 1, 24, 15, 25, 0, DateTimeKind.Utc),
            Modified = new DateTime(2025, 1, 24, 16, 0, 0, DateTimeKind.Utc),
            StockTakingItems = null
        };

        var binary = stockTaking.ToBinary();

        // Parse header
        var pos = 0;
        var version = binary[pos++];
        var marker = binary[pos++];

        var propertyNames = new List<string>();
        if ((marker & 0x10) != 0)
        {
            var propCount = (int)binary[pos++]; // Simplified VarUInt read
            for (int i = 0; i < propCount; i++)
            {
                var strLen = (int)binary[pos++];
                var propName = System.Text.Encoding.UTF8.GetString(binary, pos, strLen);
                pos += strLen;
                propertyNames.Add(propName);
            }
        }

        // Parse body
        Console.WriteLine($"\n=== BODY (starts at position {pos}) ===");
        var objectMarker = binary[pos++];
        Console.WriteLine($"Object marker: 0x{objectMarker:X2}");

        // Read ref ID (VarInt)
        var refIdByte = binary[pos];
        if ((refIdByte & 0x80) == 0) pos++;
        else pos += 2;

        // Read property count
        var bodyPropCount = binary[pos++];
        Console.WriteLine($"Body property count: {bodyPropCount}");

        for (int i = 0; i < bodyPropCount; i++)
        {
            var propIndex = binary[pos++];
            var propName = propIndex < propertyNames.Count ? propertyNames[propIndex] : $"UNKNOWN({propIndex})";
            var valueType = binary[pos];

            string valueInfo;
            if (valueType == 0x14) // DateTime
            {
                valueInfo = "DateTime";
                pos += 10;
            }
            else if (valueType >= 0xD0) // TinyInt
            {
                var tinyValue = valueType - 0xD0;
                valueInfo = $"TinyInt({tinyValue})";
                pos += 1;
            }
            else if (valueType == 0x03)
            {
                valueInfo = "False";
                pos += 1;
            }
            else if (valueType == 0x02)
            {
                valueInfo = "True";
                pos += 1;
            }
            else
            {
                valueInfo = $"0x{valueType:X2}";
                break;
            }

            Console.WriteLine($"  Body[{i}]: index={propIndex} -> '{propName}' = {valueInfo}");
        }
    }

    [TestMethod]
    public void StockTaking_ExactProductionData_RoundTrip()
    {
        var stockTakings = new List<StockTaking>
        {
            new()
            {
                Id = 7,
                StartDateTime = new DateTime(2025, 12, 3, 8, 55, 43, 539, DateTimeKind.Utc),
                IsClosed = false,
                Creator = 6,
                Created = new DateTime(2025, 12, 3, 7, 55, 43, 571, DateTimeKind.Utc),
                Modified = new DateTime(2025, 12, 3, 7, 55, 43, 571, DateTimeKind.Utc),
                StockTakingItems = null
            },
            new()
            {
                Id = 6,
                StartDateTime = new DateTime(2025, 12, 2, 8, 21, 26, 439, DateTimeKind.Utc),
                IsClosed = true,
                Creator = 6,
                Created = new DateTime(2025, 12, 2, 7, 21, 26, 468, DateTimeKind.Utc),
                Modified = new DateTime(2025, 12, 2, 7, 21, 26, 468, DateTimeKind.Utc),
                StockTakingItems = null
            },
            new()
            {
                Id = 3,
                StartDateTime = new DateTime(2025, 11, 30, 14, 1, 55, 663, DateTimeKind.Utc),
                IsClosed = true,
                Creator = 6,
                Created = new DateTime(2025, 11, 30, 13, 1, 55, 692, DateTimeKind.Utc),
                Modified = new DateTime(2025, 11, 30, 13, 1, 55, 692, DateTimeKind.Utc),
                StockTakingItems = null
            },
            new()
            {
                Id = 2,
                StartDateTime = new DateTime(2025, 11, 30, 8, 20, 2, 182, DateTimeKind.Utc),
                IsClosed = true,
                Creator = 6,
                Created = new DateTime(2025, 11, 30, 7, 20, 3, 331, DateTimeKind.Utc),
                Modified = new DateTime(2025, 11, 30, 7, 20, 3, 331, DateTimeKind.Utc),
                StockTakingItems = null
            },
            new()
            {
                Id = 1,
                StartDateTime = new DateTime(2025, 11, 30, 8, 18, 59, 693, DateTimeKind.Utc),
                IsClosed = true,
                Creator = 6,
                Created = new DateTime(2025, 11, 30, 7, 19, 1, 849, DateTimeKind.Utc),
                Modified = new DateTime(2025, 11, 30, 7, 19, 1, 877, DateTimeKind.Utc),
                StockTakingItems = null
            }
        };

        var binary = stockTakings.ToBinary();
        Console.WriteLine($"Binary length: {binary.Length}");

        var result = binary.BinaryTo<List<StockTaking>>();

        Assert.IsNotNull(result);
        Assert.AreEqual(5, result.Count);

        foreach (var item in result)
        {
            Assert.AreEqual(6, item.Creator,
                $"StockTaking Id={item.Id}: Creator should be 6, got {item.Creator}");
        }
    }
}
