using System;
using System.Collections.Generic;
using System.Linq;
using PolyTraderSharp.Models;

namespace PolyTrader.Core.Analytics
{
    /// <summary>
    /// Reine, seiteneffektfreie Auswertungslogik über den generischen Core-Trade-Log
    /// (<see cref="TradeRecord"/>). Speist Dashboard-Kennzahlen/Charts und die Launcher-Account-Übersicht.
    /// Die Filterung (Account/Modul/Demo/Zeitraum) trifft der Aufrufer; hier wird nur aggregiert.
    /// Vollständig unit-getestet, weil „profitabel ja/nein" davon abhängt.
    /// </summary>
    public static class TradeAnalytics
    {
        /// <summary>Kernkennzahlen eines Trade-Sets (ohne Fees – die liegen nur in den Modul-Logs).</summary>
        public readonly record struct Kpis(
            int TradeCount, decimal NetPnl, decimal WinRatePct, decimal AvgPnlPerTrade, decimal ProfitFactor);

        /// <summary>Profit-Faktor bei verlustfreiem Set (∞) – als großer, endlicher Anzeigewert.</summary>
        public const decimal NoLossProfitFactor = 999m;

        public static Kpis ComputeKpis(IEnumerable<TradeRecord> trades)
        {
            var list = trades as IReadOnlyList<TradeRecord> ?? trades.ToList();
            int n = list.Count;
            if (n == 0) return new Kpis(0, 0m, 0m, 0m, 0m);

            decimal net = list.Sum(t => t.RealizedPnl);
            int wins = list.Count(t => t.RealizedPnl > 0m);
            decimal grossWin = list.Where(t => t.RealizedPnl > 0m).Sum(t => t.RealizedPnl);
            decimal grossLoss = list.Where(t => t.RealizedPnl < 0m).Sum(t => -t.RealizedPnl);
            decimal pf = grossLoss > 0m ? grossWin / grossLoss : (grossWin > 0m ? NoLossProfitFactor : 0m);

            return new Kpis(n, net, 100m * wins / n, net / n, pf);
        }

        /// <summary>Equity-Kurve: nach Abschlusszeit sortiert, kumulierter realisierter PnL.</summary>
        public static List<(DateTime At, decimal Cumulative)> EquityCurve(IEnumerable<TradeRecord> trades)
        {
            var result = new List<(DateTime, decimal)>();
            decimal cum = 0m;
            foreach (var t in trades.OrderBy(t => t.ClosedAt))
            {
                cum += t.RealizedPnl;
                result.Add((t.ClosedAt, cum));
            }
            return result;
        }

        /// <summary>PnL + Anzahl je Gruppierungsschlüssel (z. B. Modul oder Account), absteigend nach PnL.</summary>
        public static List<(string Key, decimal Pnl, int Count)> PnlByKey(
            IEnumerable<TradeRecord> trades, Func<TradeRecord, string> keySelector)
        {
            return trades
                .GroupBy(keySelector)
                .Select(g => (Key: g.Key, Pnl: g.Sum(t => t.RealizedPnl), Count: g.Count()))
                .OrderByDescending(x => x.Pnl)
                .ToList();
        }

        /// <summary>PnL je Kalendertag (nach <see cref="TradeRecord.ClosedAt"/>), chronologisch.</summary>
        public static List<(DateTime Day, decimal Pnl)> PnlByDay(IEnumerable<TradeRecord> trades)
        {
            return trades
                .GroupBy(t => t.ClosedAt.Date)
                .Select(g => (Day: g.Key, Pnl: g.Sum(t => t.RealizedPnl)))
                .OrderBy(x => x.Day)
                .ToList();
        }

        /// <summary>Kurz-Zusammenfassung für die Launcher-Übersicht (z. B. „letzte 3 Tage" je Account).</summary>
        public static (decimal Pnl, decimal WinRatePct, int Count) WindowSummary(IEnumerable<TradeRecord> trades)
        {
            var list = trades as IReadOnlyList<TradeRecord> ?? trades.ToList();
            int n = list.Count;
            if (n == 0) return (0m, 0m, 0);
            int wins = list.Count(t => t.RealizedPnl > 0m);
            return (list.Sum(t => t.RealizedPnl), 100m * wins / n, n);
        }
    }
}
