using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.EntityFrameworkCore;
using Predictalytics.Application.DTOs;
using Predictalytics.Application.Services;
using Predictalytics.Infrastructure.Data;

namespace Predictalytics.Api.Endpoints;

public static class PortfolioEndpoints
{
    public static void MapPortfolioEndpoints(this IEndpointRouteBuilder routes)
    {
        var group = routes.MapGroup("/api/portfolio").WithTags("Portfolio");

        // Diversified copy-portfolio suggestion (#4): top masters, de-clumped by pairwise position
        // correlation and capped per category, so the copied set actually spreads risk.
        group.MapGet("/suggest", async (int? size, int? maxPerCategory, double? maxSimilarity, decimal? minScore,
            AppDbContext db, CancellationToken ct) =>
        {
            var minScoreV = minScore ?? 50m;
            var candidatesRaw = await db.Traders
                .Include(t => t.Analytics)
                .Include(t => t.CategoryPerformances)
                .Where(t => t.Analytics != null && t.Analytics.CopytradingScore >= minScoreV && !t.IsSuspectedBot)
                .OrderByDescending(t => t.Analytics!.CopytradingScore)
                .Take(60)
                .ToListAsync(ct);

            if (candidatesRaw.Count == 0)
                return Results.Ok(new CopyPortfolioDto(System.Array.Empty<PortfolioPickDto>(), 0, 0, 0));

            var ids = candidatesRaw.Select(t => t.Id).ToList();

            // Signed-market sets per trader (marketId:direction) → position-overlap similarity.
            var positions = await db.TraderPositions
                .Where(p => ids.Contains(p.TraderId) && p.MarketOutcome != null && p.SharesHeld != 0)
                .Select(p => new { p.TraderId, p.MarketOutcome!.MarketId, Dir = p.SharesHeld > 0 ? 1 : -1 })
                .ToListAsync(ct);

            var sets = positions
                .GroupBy(p => p.TraderId)
                .ToDictionary(g => g.Key, g => g.Select(x => $"{x.MarketId}:{x.Dir}").ToHashSet());

            double Similarity(int a, int b)
            {
                if (!sets.TryGetValue(a, out var sa) || !sets.TryGetValue(b, out var sb) || sa.Count == 0 || sb.Count == 0)
                    return 0;
                var inter = sa.Count(sb.Contains);
                return (double)inter / System.Math.Min(sa.Count, sb.Count);
            }

            var candidates = candidatesRaw
                .Select(t => new PortfolioCandidate(t.Id, t.Analytics!.CopytradingScore, PrimaryCategory(t)))
                .ToList();

            var result = CopyPortfolioBuilder.Build(
                candidates, Similarity,
                size ?? 8, maxPerCategory ?? 2, maxSimilarity ?? 0.6);

            var byId = candidatesRaw.ToDictionary(t => t.Id);
            var picks = result.Picks.Select(p =>
            {
                var t = byId[p.TraderId];
                return new PortfolioPickDto(
                    p.TraderId, t.DisplayName, t.Platform.ToString(), p.Score,
                    t.Analytics?.CopytradingCopyabilityScore ?? 0, t.TotalPnl, p.Category, p.Reason);
            }).ToList();

            return Results.Ok(new CopyPortfolioDto(
                picks, candidatesRaw.Count,
                result.DroppedForCorrelation.Count, result.DroppedForCategoryCap.Count));
        });
    }

    private static string PrimaryCategory(Predictalytics.Domain.Entities.Trader t)
    {
        var perfs = t.CategoryPerformances;
        if (perfs == null || perfs.Count == 0) return "Unbekannt";
        var top = perfs.OrderByDescending(p => p.TotalVolume).First();
        return top.TotalVolume > 0 ? top.Category.ToString() : "Unbekannt";
    }
}
