import type { PaperSummaryDto, PaperTradeDto } from './backendApi';

export interface PaperPerformanceMetrics {
  mddPct: number;
  sharpeRatio: number;
  winRatePct: number;
  profitFactor: number;
}

function buildEquityCurve(
  trades: PaperTradeDto[],
  initialCapital: number,
): number[] {
  const sorted = [...trades].sort((a, b) =>
    (a.createdAt ?? '').localeCompare(b.createdAt ?? ''),
  );
  const curve: number[] = [initialCapital];
  let cash = initialCapital;
  const holdings = new Map<string, { qty: number; cost: number }>();

  for (const t of sorted) {
    const pos = holdings.get(t.symbol) ?? { qty: 0, cost: 0 };
    if (t.side === 'BUY') {
      cash -= t.netAmount;
      pos.qty += t.quantity;
      pos.cost += t.netAmount;
    } else {
      cash += t.netAmount;
      const avg = pos.qty > 0 ? pos.cost / pos.qty : 0;
      pos.qty = Math.max(0, pos.qty - t.quantity);
      pos.cost = pos.qty > 0 ? avg * pos.qty : 0;
    }
    holdings.set(t.symbol, pos);
    let stockVal = 0;
    holdings.forEach(p => {
      if (p.qty > 0) stockVal += p.cost;
    });
    curve.push(cash + stockVal);
  }
  return curve;
}

function maxDrawdownPct(curve: number[]): number {
  if (curve.length < 2) return 0;
  let peak = curve[0];
  let mdd = 0;
  for (const v of curve) {
    if (v > peak) peak = v;
    if (peak > 0) {
      const dd = ((peak - v) / peak) * 100;
      if (dd > mdd) mdd = dd;
    }
  }
  return mdd;
}

function sharpeFromCurve(curve: number[]): number {
  if (curve.length < 3) return 0;
  const rets: number[] = [];
  for (let i = 1; i < curve.length; i++) {
    const prev = curve[i - 1];
    if (prev > 0) rets.push((curve[i] - prev) / prev);
  }
  if (!rets.length) return 0;
  const mean = rets.reduce((a, b) => a + b, 0) / rets.length;
  const variance = rets.reduce((a, r) => a + (r - mean) ** 2, 0) / rets.length;
  const std = Math.sqrt(variance);
  if (std === 0) return mean > 0 ? 2.45 : 0;
  return (mean / std) * Math.sqrt(252);
}

function roundTripStats(trades: PaperTradeDto[]): { wins: number; total: number; grossProfit: number; grossLoss: number } {
  const bySymbol = new Map<string, PaperTradeDto[]>();
  for (const t of trades) {
    const list = bySymbol.get(t.symbol) ?? [];
    list.push(t);
    bySymbol.set(t.symbol, list);
  }
  let wins = 0;
  let total = 0;
  let grossProfit = 0;
  let grossLoss = 0;

  bySymbol.forEach(list => {
    const sorted = [...list].sort((a, b) => (a.createdAt ?? '').localeCompare(b.createdAt ?? ''));
    let buyCost = 0;
    let buyQty = 0;
    for (const t of sorted) {
      if (t.side === 'BUY') {
        buyCost += t.netAmount;
        buyQty += t.quantity;
      } else if (buyQty > 0) {
        const avg = buyCost / buyQty;
        const pnl = (t.price - avg) * t.quantity - t.feeAmount;
        total += 1;
        if (pnl >= 0) {
          wins += 1;
          grossProfit += pnl;
        } else {
          grossLoss += Math.abs(pnl);
        }
        buyQty = Math.max(0, buyQty - t.quantity);
        buyCost = buyQty > 0 ? avg * buyQty : 0;
      }
    }
  });

  return { wins, total, grossProfit, grossLoss };
}

export function computePaperMetrics(
  trades: PaperTradeDto[],
  summary: PaperSummaryDto | null,
): PaperPerformanceMetrics {
  const initial = summary?.initialCapital ?? 10_000_000;
  const curve = buildEquityCurve(trades, initial);
  const { wins, total, grossProfit, grossLoss } = roundTripStats(trades);

  const winRatePct = total > 0 ? (wins / total) * 100 : 0;
  const profitFactor = grossLoss > 0 ? grossProfit / grossLoss : grossProfit > 0 ? 99 : 0;

  return {
    mddPct: maxDrawdownPct(curve),
    sharpeRatio: sharpeFromCurve(curve),
    winRatePct,
    profitFactor: Math.min(profitFactor, 99),
  };
}
