using Predictalytics.Domain.Entities;
using Predictalytics.Domain.Enums;
using Predictalytics.Domain.Interfaces;
using Predictalytics.Infrastructure.Logging;
using Microsoft.Extensions.Logging;

namespace Predictalytics.Infrastructure.Providers.Limitless;

/// <summary>
/// Placeholder provider for Limitless prediction market.
/// All log entries include Platform=Limitless for log-file routing.
/// </summary>
public class LimitlessProvider : IPlatformProvider
{
    private readonly LimitlessApiClient _api;
    private readonly ILogger<LimitlessProvider> _logger;

    public PlatformType Platform => PlatformType.Limitless;
    public string PlatformName => "Limitless";
    public bool IsImplemented => true;

    public LimitlessProvider(LimitlessApiClient api, ILogger<LimitlessProvider> logger)
    {
        _api = api;
        _logger = logger;
    }

    public Task<IReadOnlyList<Trade>> GetTradesPagedAsync(string platformUserId, int limitPerRequest = 500, CancellationToken ct = default)
    {
        throw new NotImplementedException();
    }

    public async Task<IReadOnlyList<Trade>> GetTraderTradesAsync(string platformUserId, int limit = 50, CancellationToken ct = default)
    {
        using var _ = PlatformLogContext.Push(PlatformName);
        _logger.LogDebug("Fetching latest trade activity via positions for {Wallet}", platformUserId);

        var portfolio = await _api.GetPositionsAsync(platformUserId, ct);
        if (portfolio == null) return [];

        var items = (portfolio.Clob ?? []).Concat(portfolio.Amm ?? []).Concat(portfolio.Group ?? []);
        var trades = new List<Trade>();

        foreach (var item in items)
        {
            if (item.Positions == null || item.Market == null) continue;

            // Check Yes and No positions for latest trades
            var posList = new[] { 
                (Details: item.Positions.Yes, Outcome: "Yes", TokenId: item.Market.Tokens?.Yes), 
                (Details: item.Positions.No, Outcome: "No", TokenId: item.Market.Tokens?.No) 
            };

            foreach (var pos in posList)
            {
                if (pos.Details?.LatestTrade == null) continue;

                var lt = pos.Details.LatestTrade;
                decimal.TryParse(lt.Price, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var price);
                decimal.TryParse(lt.Size, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var size);

                trades.Add(new Trade
                {
                    Platform = PlatformType.Limitless,
                    // Compact format: {txHash}_{assetId} — wallet via TransientWallet
                    PlatformTradeId = lt.TransactionHash != null
                        ? $"{lt.TransactionHash}_{pos.TokenId}"
                        : $"{lt.Timestamp}_{pos.TokenId}",
                    MarketId = item.Market.Address ?? item.Market.Slug ?? "",
                    AssetId = pos.TokenId ?? "",
                    Outcome = pos.Outcome,
                    Side = ParseSide(lt.Side),
                    Price = price,
                    Size = size,
                    Amount = price * size,
                    ExecutedAt = (lt.Timestamp > 0 && lt.Timestamp < 253402300799)
                        ? DateTimeOffset.FromUnixTimeSeconds(lt.Timestamp).UtcDateTime
                        : DateTime.UtcNow,
                    TransactionHash = lt.TransactionHash,
                    TransientWallet = platformUserId,
                });
            }
        }

        return trades.OrderByDescending(t => t.ExecutedAt).Take(limit).ToList();
    }

    public async Task<IReadOnlyList<TraderPositionInfo>> GetTraderPositionsAsync(string platformUserId, CancellationToken ct = default)
    {
        using var _ = PlatformLogContext.Push(PlatformName);
        var portfolio = await _api.GetPositionsAsync(platformUserId, ct);
        if (portfolio == null) return [];

        var items = (portfolio.Clob ?? []).Concat(portfolio.Amm ?? []).Concat(portfolio.Group ?? []);
        var result = new List<TraderPositionInfo>();

        foreach (var item in items)
        {
            if (item.Positions == null || item.Market == null) continue;

            var posList = new[] { 
                (Details: item.Positions.Yes, Outcome: "Yes", TokenId: item.Market.Tokens?.Yes), 
                (Details: item.Positions.No, Outcome: "No", TokenId: item.Market.Tokens?.No) 
            };

            foreach (var pos in posList)
            {
                if (pos.Details == null) continue;
                
                decimal.TryParse(pos.Details.Size, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var size);
                if (size == 0) continue;

                decimal.TryParse(pos.Details.FillPrice, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var avgPrice);
                decimal.TryParse(pos.Details.MarketValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var val);
                decimal.TryParse(pos.Details.UnrealizedPnl, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var pnl);

                result.Add(new TraderPositionInfo(
                    platformUserId,
                    item.Market.Address ?? item.Market.Slug ?? "",
                    item.Market.Title ?? "",
                    pos.Outcome,
                    size,
                    avgPrice,
                    val,
                    0 // PercentPnl not directly available as decimal in this view
                ));
            }
        }

        return result;
    }

    public async Task<IReadOnlyList<DiscoveredTrader>> DiscoverTradersAsync(int limit = 20, CancellationToken ct = default)
    {
        using var _ = PlatformLogContext.Push(PlatformName);
        _logger.LogInformation("Trader discovery for Limitless via active markets...");
        
        var markets = await _api.GetActiveMarketsAsync(10, 0, ct);
        var traders = new List<DiscoveredTrader>();

        foreach (var m in markets.Take(5))
        {
            if (ct.IsCancellationRequested) break;
            var events = await _api.GetMarketEventsAsync(m.Slug ?? m.Address ?? "", 20, ct);
            
            foreach (var e in events.Where(ev => ev.Profile?.Account != null))
            {
                traders.Add(new DiscoveredTrader(
                    e.Profile!.Account!,
                    e.Profile.Account![..10] + "...",
                    (decimal)(e.Price * e.Size ?? 0),
                    1, 0
                ));
            }
        }

        return traders.GroupBy(t => t.PlatformUserId)
            .Select(g => g.First())
            .Take(limit)
            .ToList();
    }

    public async Task<Market?> GetMarketAsync(string platformMarketId, CancellationToken ct = default)
    {
        using var _ = PlatformLogContext.Push(PlatformName);
        var raw = await _api.GetMarketAsync(platformMarketId, ct);
        if (raw == null) return null;

        return MapLimitlessMarket(raw);
    }

    public async Task<IReadOnlyList<Event>> GetEventsAsync(int limit = 100, string? cursor = null, bool includeClosed = false, CancellationToken ct = default)
    {
        using var _ = PlatformLogContext.Push(PlatformName);
        var offset = 0;
        if (!string.IsNullOrEmpty(cursor) && int.TryParse(cursor, out var parsed))
            offset = parsed;

        _logger.LogInformation("Fetching markets batch (limit={Limit}, offset={Offset})", limit, offset);
        var raw = await _api.GetActiveMarketsAsync(limit, offset, ct);
        _logger.LogInformation("Fetched {Count} markets from Limitless API", raw.Count);

        var events = new List<Event>();
        foreach (var r in raw)
        {
            var m = MapLimitlessMarket(r);
            events.Add(new Event
            {
                Platform = PlatformType.Limitless,
                PlatformEventId = m.PlatformMarketId, // Use market ID as Event ID
                Slug = "limitless-" + m.ConditionId,
                Title = m.Question,
                DbCreatedAt = DateTime.UtcNow,
                LastUpdatedAt = DateTime.UtcNow,
                Markets = new List<Market> { m }
            });
        }
        return events;
    }

    public async Task<IReadOnlyList<DiscoveredTrader>> GetTopHoldersAsync(string platformMarketId, int limit = 20, CancellationToken ct = default)
    {
        var events = await _api.GetMarketEventsAsync(platformMarketId, limit * 2, ct);
        return events
            .Where(e => e.Profile?.Account != null)
            .GroupBy(e => e.Profile!.Account)
            .Select(g => new DiscoveredTrader(g.Key!, g.Key![..10] + "...", 0, g.Count(), 0))
            .Take(limit)
            .ToList();
    }

    public async Task<IReadOnlyList<Trade>> GetMarketTradesAsync(string platformMarketId, int limit = 50, CancellationToken ct = default)
    {
        var events = await _api.GetMarketEventsAsync(platformMarketId, limit, ct);
        return events
            .Where(e => e.Profile?.Account != null)
            .Select(e =>
            {
                var wallet = e.Profile!.Account!;
                var asset = e.Asset ?? "";
                var side = e.Side ?? "";
                // Compact format: {txHash}_{assetId} — wallet via TransientWallet
                var tradeId = e.TxHash != null
                    ? $"{e.TxHash}_{asset}"
                    : $"{e.CreatedAt}_{asset}_{side}";

                return new Trade
                {
                    Platform = PlatformType.Limitless,
                    PlatformTradeId = tradeId,
                    MarketId = platformMarketId,
                    AssetId = asset,
                    Outcome = "",
                    Side = ParseSide(side),
                    Price = (decimal)(e.Price ?? 0),
                    Size = (decimal)(e.Size ?? 0),
                    Amount = (decimal)(e.Price * e.Size ?? 0),
                    ExecutedAt = DateTime.TryParse(e.CreatedAt, out var dt) ? dt : DateTime.UtcNow,
                    TransactionHash = e.TxHash,
                    TransientWallet = wallet,
                };
            })
            .ToList();
    }

    public Task<IReadOnlyList<MarketOutcomePriceSnapshot>> GetPriceHistoryAsync(string tokenId, CancellationToken ct = default)
    {
        return Task.FromResult<IReadOnlyList<MarketOutcomePriceSnapshot>>(Array.Empty<MarketOutcomePriceSnapshot>());
    }

    private Market MapLimitlessMarket(LimitlessMarketResponse raw)
    {
        var conditionId = raw.Address ?? raw.Slug ?? Guid.NewGuid().ToString();
        var firstCat = raw.Categories?.FirstOrDefault() ?? "";
        var catMap = Predictalytics.Infrastructure.Helpers.MarketCategoryMapper.Map(firstCat, string.Join(",", raw.Categories ?? []));

        var market = new Market
        {
            Platform = PlatformType.Limitless,
            ConditionId = conditionId,
            PlatformMarketId = GetStableHashCode(conditionId),
            MarketSlug = raw.Slug ?? "",
            Question = raw.Title ?? "",
            Description = raw.Description ?? "",
            Category = catMap.Category,
            Subcategory = catMap.Subcategory,
            ImageUrl = raw.ImageUrl ?? "",
            Volume = decimal.TryParse(raw.VolumeFormatted?.Replace(" USDC", ""), out var vol) ? vol : 0,
            Liquidity = (decimal)(raw.Liquidity ?? 0),
            EndDate = (raw.ExpirationTimestamp.HasValue && raw.ExpirationTimestamp.Value > 0 && raw.ExpirationTimestamp.Value < 253402300799)
                ? DateTimeOffset.FromUnixTimeSeconds(raw.ExpirationTimestamp.Value).UtcDateTime 
                : (DateTime.TryParse(raw.ExpirationDate, out var ed) ? ed : null),
            IsResolved = raw.Closed,
            LastUpdatedAt = DateTime.UtcNow
        };

        // Map Outcomes from tokens object and prices array
        if (raw.Tokens != null)
        {
            // Yes Outcome
            market.Outcomes.Add(new MarketOutcome
            {
                Label = "Yes",
                OutcomeIndex = 0,
                TokenId = raw.Tokens.Yes ?? "",
                CurrentPrice = (decimal)(raw.Prices != null && raw.Prices.Count > 0 ? raw.Prices[0] : 0)
            });

            // No Outcome
            market.Outcomes.Add(new MarketOutcome
            {
                Label = "No",
                OutcomeIndex = 1,
                TokenId = raw.Tokens.No ?? "",
                CurrentPrice = (decimal)(raw.Prices != null && raw.Prices.Count > 1 ? raw.Prices[1] : 0)
            });
        }

        return market;
    }

    private TradeSide ParseSide(object? sideObj)
    {
        var sideStr = sideObj?.ToString()?.ToUpper();
        if (sideStr == "0" || sideStr == "BUY") return TradeSide.Buy;
        return TradeSide.Sell;
    }

    private static long GetStableHashCode(string str)
    {
        unchecked
        {
            long hash = 23;
            foreach (char c in str)
                hash = hash * 31 + c;
            return hash;
        }
    }
}
