using System.Collections.Generic;
using System.Linq;
using PolyTrader.Modules.CopyTrading.Logic;
using PolyTraderSharp.Models;
using Xunit;

namespace PolyTrader.Tests
{
    /// <summary>
    /// Sichert den Aufbau des Alchemy-Blockchain-Abos ab: falsche Filter/Padding bedeuten
    /// verpasste (oder falsch zugeordnete) Master-Trades.
    /// </summary>
    public class BlockchainSubscriptionTests
    {
        private static List<TrackedTrader> Traders(int n) =>
            Enumerable.Range(1, n).Select(i => new TrackedTrader { Id = i, WalletAddress = $"0x{i:x40}" }).ToList();

        // ---------------- PadAddress ----------------

        [Fact]
        public void PadAddress_strips_prefix_lowercases_and_pads_to_32_bytes()
        {
            var r = BlockchainSubscription.PadAddress("0x628914CF1e96A9D1Ab8F0489A9f64be5633bac41");
            Assert.Equal("0x000000000000000000000000628914cf1e96a9d1ab8f0489a9f64be5633bac41", r);
            Assert.Equal(66, r.Length); // "0x" + 64 Hex-Zeichen
        }

        [Fact]
        public void PadAddress_pads_short_input_with_leading_zeros()
        {
            Assert.Equal("0x" + new string('0', 61) + "abc", BlockchainSubscription.PadAddress("0xABC"));
        }

        // ---------------- Build: Chunk-Anzahl ----------------

        [Theory]
        [InlineData(0, 0)]
        [InlineData(1, 2)]   // 1 Chunk -> Buy + Sell
        [InlineData(3, 2)]   // 1 Chunk (voll)
        [InlineData(4, 4)]   // 2 Chunks (3 + 1)
        [InlineData(6, 4)]   // 2 Chunks (3 + 3)
        [InlineData(7, 6)]   // 3 Chunks (3 + 3 + 1)
        public void Build_creates_two_filters_per_chunk(int traderCount, int expectedFilters)
        {
            Assert.Equal(expectedFilters, BlockchainSubscription.Build(Traders(traderCount)).Filters.Count);
        }

        // ---------------- Build: Filter-Inhalt ----------------

        [Fact]
        public void Build_pairs_buy_topic3_and_sell_topic2()
        {
            var sub = BlockchainSubscription.Build(Traders(1));
            var buy = sub.Filters[0];
            var sell = sub.Filters[1];

            Assert.NotNull(buy.Topic3);
            Assert.Null(buy.Topic2);
            Assert.NotNull(sell.Topic2);
            Assert.Null(sell.Topic3);
        }

        [Fact]
        public void Build_sets_ctf_address_and_transfer_topics()
        {
            var buy = BlockchainSubscription.Build(Traders(1)).Filters[0];
            Assert.Equal(BlockchainSubscription.CtfContractAddress, buy.Address);
            Assert.Equal(
                new[] { BlockchainSubscription.TransferSingleTopic, BlockchainSubscription.TransferBatchTopic },
                buy.Topic0.ToArray());
        }

        [Fact]
        public void Build_chunks_addresses_by_three()
        {
            var sub = BlockchainSubscription.Build(Traders(4)); // 3 + 1
            Assert.Equal(3, sub.Filters[0].Topic3!.Count);      // Chunk 1 (Buy)
            Assert.Equal(3, sub.Filters[1].Topic2!.Count);      // Chunk 1 (Sell)
            Assert.Single(sub.Filters[2].Topic3!);              // Chunk 2 (Buy)
            Assert.Single(sub.Filters[3].Topic2!);              // Chunk 2 (Sell)
        }

        [Fact]
        public void Build_uses_padded_addresses()
        {
            var addr = BlockchainSubscription.Build(Traders(1)).Filters[0].Topic3![0];
            Assert.Equal(66, addr.Length);
            Assert.StartsWith("0x", addr);
        }

        [Fact]
        public void Build_empty_returns_no_filters()
        {
            Assert.Empty(BlockchainSubscription.Build(Traders(0)).Filters);
        }
    }
}
