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

namespace Predictalytics.Application.Services;

/// <summary>
/// Discovers new notable traders on prediction market platforms.
/// </summary>
public class DiscoveryService : IDiscoveryService
{
    private readonly IEnumerable<IPlatformProvider> _providers;
    private readonly ITraderRepository _traderRepo;
    private readonly IRateLimiter _rateLimiter;
    private readonly ILogger<DiscoveryService> _logger;

    public DiscoveryService(
        IEnumerable<IPlatformProvider> providers,
        ITraderRepository traderRepo,
        IRateLimiter rateLimiter,
        ILogger<DiscoveryService> logger)
    {
        _providers = providers;
        _traderRepo = traderRepo;
        _rateLimiter = rateLimiter;
        _logger = logger;
    }

    public async Task<IReadOnlyList<DiscoveredTrader>> RunDiscoveryAsync(PlatformType platform, CancellationToken ct = default)
    {
        var provider = _providers.FirstOrDefault(p => p.Platform == platform);
        if (provider == null || !provider.IsImplemented)
        {
            _logger.LogWarning("No implemented provider for platform {Platform}", platform);
            return Array.Empty<DiscoveredTrader>();
        }

        await _rateLimiter.WaitAsync(platform, ct);

        _logger.LogInformation("[{Platform}] Running trader discovery scan...", platform);
        var discovered = await provider.DiscoverTradersAsync(50, ct);

        int newCount = 0;
        foreach (var d in discovered)
        {
            var existing = await _traderRepo.GetByPlatformIdAsync(platform, d.PlatformUserId, ct);
            if (existing == null)
            {
                await ImportTraderAsync(platform, d.PlatformUserId, d.DisplayName, true, ct);
                newCount++;
            }
        }

        _logger.LogInformation("[{Platform}] Discovery complete: {Total} found, {New} new traders imported",
            platform, discovered.Count, newCount);

        return discovered;
    }

    public async Task<int> ImportTraderAsync(PlatformType platform, string platformUserId, string displayName, bool isAutoDiscovered = true, CancellationToken ct = default)
    {
        var existing = await _traderRepo.GetByPlatformIdAsync(platform, platformUserId, ct);
        if (existing != null) return existing.Id;

        if (string.IsNullOrEmpty(displayName) || displayName.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
        {
            var provider = _providers.FirstOrDefault(p => p.Platform == platform);
            if (provider != null && provider.IsImplemented)
            {
                try
                {
                    await _rateLimiter.WaitAsync(platform, ct);
                    var trades = await provider.GetTraderTradesAsync(platformUserId, 50, ct);
                    var realName = trades.FirstOrDefault(t => !string.IsNullOrEmpty(t.TransientDisplayName))?.TransientDisplayName;
                    if (!string.IsNullOrEmpty(realName))
                    {
                        displayName = realName;
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "Failed to fetch real display name for {Wallet} during import", platformUserId);
                }
            }
        }

        var trader = new Trader
        {
            Platform = platform,
            PlatformUserId = platformUserId,
            DisplayName = string.IsNullOrEmpty(displayName) ? (platformUserId.Length > 8 ? platformUserId[..8] + "..." : platformUserId) : displayName,
            IsAutoDiscovered = isAutoDiscovered,
            CreatedAt = DateTime.UtcNow
        };

        await _traderRepo.AddAsync(trader, ct);
        _logger.LogInformation("[{Platform}] Imported new trader: {Name} ({Id})",
            platform, trader.DisplayName, trader.PlatformUserId);

        return trader.Id;
    }
}
