using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace PolyTrader.Core.Streaming
{
    /// <summary>
    /// EVM/Alchemy-WSS-Verbindungsklasse (eth_subscribe "logs"). Domänen-agnostisch:
    /// Contract/Topics/Adress-Filter kommen vom Aufrufer über die Subscription.
    /// </summary>
    public class AlchemyWssClient : IBlockchainWssClient
    {
        public bool IsHealthy { get; private set; }

        public async Task ConnectAndListenAsync(string rpcUrl, BlockchainWssSubscription subscription,
            Action<BlockchainLogEvent> onLog, CancellationToken ct)
        {
            using var ws = new ClientWebSocket();
            var wssUrl = rpcUrl.Replace("https://", "wss://").Replace("http://", "ws://");

            await ws.ConnectAsync(new Uri(wssUrl), ct);
            IsHealthy = true;

            try
            {
                int reqId = 1;
                foreach (var filter in subscription.Filters)
                {
                    var payload = BuildSubscribePayload(reqId++, filter);
                    await ws.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(payload)),
                        WebSocketMessageType.Text, true, ct);
                }

                var buffer = new byte[1024 * 64];
                while (ws.State == WebSocketState.Open && !ct.IsCancellationRequested)
                {
                    var result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), ct);
                    if (result.MessageType == WebSocketMessageType.Close) break;

                    var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
                    try
                    {
                        var evt = TryParseLog(message);
                        if (evt != null) onLog(evt);
                    }
                    catch
                    {
                        // Resilient: eine fehlerhafte Nachricht oder ein Handler-Fehler
                        // darf die Session nicht beenden (Verhalten wie zuvor).
                    }
                }
            }
            finally
            {
                IsHealthy = false;
            }
        }

        private static string BuildSubscribePayload(int id, LogSubscriptionFilter f)
        {
            // Positionsbasiertes Topics-Array; trailing-Wildcards (null) werden abgeschnitten,
            // exakt wie im ursprünglichen Handbuild (Sells: 3 Positionen, Buys: 4).
            var positions = new List<string>
            {
                JsonSerializer.Serialize(f.Topic0),
                f.Topic1 != null ? JsonSerializer.Serialize(f.Topic1) : "null",
                f.Topic2 != null ? JsonSerializer.Serialize(f.Topic2) : "null",
                f.Topic3 != null ? JsonSerializer.Serialize(f.Topic3) : "null"
            };
            while (positions.Count > 1 && positions[^1] == "null") positions.RemoveAt(positions.Count - 1);

            string topicsJson = "[" + string.Join(",", positions) + "]";
            return $@"{{""jsonrpc"":""2.0"",""id"":{id},""method"":""eth_subscribe"",""params"":[""logs"",{{""address"":""{f.Address}"",""topics"":{topicsJson}}}]}}";
        }

        private static BlockchainLogEvent? TryParseLog(string jsonStr)
        {
            using var doc = JsonDocument.Parse(jsonStr);
            var root = doc.RootElement;

            if (!root.TryGetProperty("params", out var paramsEl)) return null;
            if (!paramsEl.TryGetProperty("result", out var resultEl)) return null;
            if (!resultEl.TryGetProperty("topics", out var topicsEl)) return null;

            var topics = topicsEl.EnumerateArray().Select(t => t.GetString()).ToList();
            string txHash = resultEl.TryGetProperty("transactionHash", out var th) ? th.GetString() ?? "" : "";
            string address = resultEl.TryGetProperty("address", out var ad) ? ad.GetString() ?? "" : "";
            string data = resultEl.TryGetProperty("data", out var dt) ? dt.GetString() ?? "" : "";

            return new BlockchainLogEvent { Topics = topics, TransactionHash = txHash, Address = address, Data = data };
        }
    }

    public class AlchemyWssClientFactory : IBlockchainWssClientFactory
    {
        public IBlockchainWssClient Create() => new AlchemyWssClient();
    }
}
