import type { PriceData } from '@/types';

export const calculateSMA = (
  data: PriceData[],
  period: number
): { time: number; value: number }[] => {
  const result: { time: number; value: number }[] = [];

  for (let i = period - 1; i < data.length; i++) {
    let sum = 0;
    for (let j = 0; j < period; j++) {
      sum += data[i - j].close;
    }
    const sma = sum / period;
    result.push({
      time: data[i].time,
      value: Number(sma.toFixed(2)),
    });
  }

  return result;
};

export const calculateEMA = (
  data: PriceData[],
  period: number
): { time: number; value: number }[] => {
  const result: { time: number; value: number }[] = [];
  const multiplier = 2 / (period + 1);

  if (data.length < period) return result;

  // Calculate initial SMA for first EMA value
  let sum = 0;
  for (let i = 0; i < period; i++) {
    sum += data[i].close;
  }
  let ema = sum / period;
  result.push({ time: data[period - 1].time, value: Number(ema.toFixed(2)) });

  // Calculate EMA for remaining data
  for (let i = period; i < data.length; i++) {
    ema = (data[i].close - ema) * multiplier + ema;
    result.push({ time: data[i].time, value: Number(ema.toFixed(2)) });
  }

  return result;
};

export const calculateRSI = (
  data: PriceData[],
  period: number = 14
): { time: number; value: number }[] => {
  const result: { time: number; value: number }[] = [];

  if (data.length < period + 1) return result;

  let gains = 0;
  let losses = 0;

  // Calculate initial average gain and loss
  for (let i = 1; i <= period; i++) {
    const change = data[i].close - data[i - 1].close;
    if (change > 0) {
      gains += change;
    } else {
      losses += Math.abs(change);
    }
  }

  let avgGain = gains / period;
  let avgLoss = losses / period;

  for (let i = period; i < data.length; i++) {
    const change = data[i].close - data[i - 1].close;
    const gain = change > 0 ? change : 0;
    const loss = change < 0 ? Math.abs(change) : 0;

    avgGain = (avgGain * (period - 1) + gain) / period;
    avgLoss = (avgLoss * (period - 1) + loss) / period;

    const rs = avgLoss === 0 ? 100 : avgGain / avgLoss;
    const rsi = 100 - 100 / (1 + rs);

    result.push({ time: data[i].time, value: Number(rsi.toFixed(2)) });
  }

  return result;
};

export const formatPrice = (price: number): string => {
  return `$${price.toFixed(2)}`;
};

export const formatPercent = (value: number): string => {
  const sign = value >= 0 ? '+' : '';
  return `${sign}${value.toFixed(2)}%`;
};

export const formatNumber = (value: number): string => {
  return new Intl.NumberFormat('en-US', {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  }).format(value);
};

// MACD (Moving Average Convergence Divergence)
export const calculateMACD = (
  data: PriceData[],
  fastPeriod: number = 12,
  slowPeriod: number = 26,
  signalPeriod: number = 9
): {
  macd: { time: number; value: number }[];
  signal: { time: number; value: number }[];
  histogram: { time: number; value: number }[];
} => {
  const fastEMA = calculateEMA(data, fastPeriod);
  const slowEMA = calculateEMA(data, slowPeriod);

  const macdLine: { time: number; value: number }[] = [];

  // Calculate MACD line (fast EMA - slow EMA)
  const startIndex = slowPeriod - 1;
  for (let i = 0; i < fastEMA.length; i++) {
    if (i + startIndex < slowEMA.length) {
      macdLine.push({
        time: fastEMA[i + fastPeriod - slowPeriod].time,
        value: Number((fastEMA[i + fastPeriod - slowPeriod].value - slowEMA[i].value).toFixed(2)),
      });
    }
  }

  // Calculate signal line (EMA of MACD)
  const signalLine: { time: number; value: number }[] = [];
  if (macdLine.length >= signalPeriod) {
    const multiplier = 2 / (signalPeriod + 1);
    let ema = macdLine.slice(0, signalPeriod).reduce((sum, d) => sum + d.value, 0) / signalPeriod;
    signalLine.push({ time: macdLine[signalPeriod - 1].time, value: Number(ema.toFixed(2)) });

    for (let i = signalPeriod; i < macdLine.length; i++) {
      ema = (macdLine[i].value - ema) * multiplier + ema;
      signalLine.push({ time: macdLine[i].time, value: Number(ema.toFixed(2)) });
    }
  }

  // Calculate histogram (MACD - Signal)
  const histogram: { time: number; value: number }[] = [];
  for (let i = 0; i < signalLine.length; i++) {
    const macdValue = macdLine[i + macdLine.length - signalLine.length].value;
    histogram.push({
      time: signalLine[i].time,
      value: Number((macdValue - signalLine[i].value).toFixed(2)),
    });
  }

  return { macd: macdLine, signal: signalLine, histogram };
};

// Bollinger Bands
export const calculateBollingerBands = (
  data: PriceData[],
  period: number = 20,
  stdDev: number = 2
): {
  upper: { time: number; value: number }[];
  middle: { time: number; value: number }[];
  lower: { time: number; value: number }[];
} => {
  const middle = calculateSMA(data, period);
  const upper: { time: number; value: number }[] = [];
  const lower: { time: number; value: number }[] = [];

  for (let i = period - 1; i < data.length; i++) {
    const slice = data.slice(i - period + 1, i + 1);
    const sma = slice.reduce((sum, d) => sum + d.close, 0) / period;
    const variance = slice.reduce((sum, d) => sum + Math.pow(d.close - sma, 2), 0) / period;
    const sd = Math.sqrt(variance);

    const time = data[i].time;
    upper.push({ time, value: Number((sma + stdDev * sd).toFixed(2)) });
    lower.push({ time, value: Number((sma - stdDev * sd).toFixed(2)) });
  }

  return { upper, middle, lower };
};

// ATR (Average True Range)
export const calculateATR = (
  data: PriceData[],
  period: number = 14
): { time: number; value: number }[] => {
  const result: { time: number; value: number }[] = [];

  if (data.length < period + 1) return result;

  const trueRanges: number[] = [];

  // Calculate True Range for each period
  for (let i = 1; i < data.length; i++) {
    const high = data[i].high;
    const low = data[i].low;
    const prevClose = data[i - 1].close;

    const tr = Math.max(
      high - low,
      Math.abs(high - prevClose),
      Math.abs(low - prevClose)
    );

    trueRanges.push(tr);
  }

  // Calculate initial ATR (SMA of TR)
  let atr = trueRanges.slice(0, period).reduce((sum, tr) => sum + tr, 0) / period;
  result.push({ time: data[period].time, value: Number(atr.toFixed(2)) });

  // Calculate remaining ATR using smoothing
  for (let i = period; i < trueRanges.length; i++) {
    atr = (atr * (period - 1) + trueRanges[i]) / period;
    result.push({ time: data[i + 1].time, value: Number(atr.toFixed(2)) });
  }

  return result;
};

// Fibonacci Retracement Levels
export const calculateFibonacci = (
  high: number,
  low: number
): { level: string; price: number }[] => {
  const diff = high - low;
  const levels = [
    { level: '0%', ratio: 0 },
    { level: '23.6%', ratio: 0.236 },
    { level: '38.2%', ratio: 0.382 },
    { level: '50%', ratio: 0.5 },
    { level: '61.8%', ratio: 0.618 },
    { level: '78.6%', ratio: 0.786 },
    { level: '100%', ratio: 1 },
  ];

  return levels.map((l) => ({
    level: l.level,
    price: Number((high - diff * l.ratio).toFixed(2)),
  }));
};

// Stochastic Oscillator
export const calculateStochastic = (
  data: PriceData[],
  kPeriod: number = 14,
  dPeriod: number = 3
): {
  k: { time: number; value: number }[];
  d: { time: number; value: number }[];
} => {
  const kLine: { time: number; value: number }[] = [];

  for (let i = kPeriod - 1; i < data.length; i++) {
    const slice = data.slice(i - kPeriod + 1, i + 1);
    const high = Math.max(...slice.map((d) => d.high));
    const low = Math.min(...slice.map((d) => d.low));
    const close = data[i].close;

    const k = low === high ? 50 : ((close - low) / (high - low)) * 100;
    kLine.push({ time: data[i].time, value: Number(k.toFixed(2)) });
  }

  // Calculate %D (SMA of %K)
  const dLine: { time: number; value: number }[] = [];
  for (let i = dPeriod - 1; i < kLine.length; i++) {
    const sum = kLine.slice(i - dPeriod + 1, i + 1).reduce((s, d) => s + d.value, 0);
    dLine.push({ time: kLine[i].time, value: Number((sum / dPeriod).toFixed(2)) });
  }

  return { k: kLine, d: dLine };
};

// Pivot Points (Standard)
export const calculatePivotPoints = (
  high: number,
  low: number,
  close: number
): {
  pivot: number;
  r1: number;
  r2: number;
  r3: number;
  s1: number;
  s2: number;
  s3: number;
} => {
  const pivot = (high + low + close) / 3;
  const r1 = 2 * pivot - low;
  const s1 = 2 * pivot - high;
  const r2 = pivot + (high - low);
  const s2 = pivot - (high - low);
  const r3 = high + 2 * (pivot - low);
  const s3 = low - 2 * (high - pivot);

  return {
    pivot: Number(pivot.toFixed(2)),
    r1: Number(r1.toFixed(2)),
    r2: Number(r2.toFixed(2)),
    r3: Number(r3.toFixed(2)),
    s1: Number(s1.toFixed(2)),
    s2: Number(s2.toFixed(2)),
    s3: Number(s3.toFixed(2)),
  };
};

// Volume Weighted Average Price (VWAP)
export const calculateVWAP = (
  data: PriceData[]
): { time: number; value: number }[] => {
  const result: { time: number; value: number }[] = [];
  let cumulativeTPV = 0; // Typical Price * Volume
  let cumulativeVolume = 0;

  for (let i = 0; i < data.length; i++) {
    const typicalPrice = (data[i].high + data[i].low + data[i].close) / 3;
    const volume = data[i].volume || 1; // Default to 1 if volume not available

    cumulativeTPV += typicalPrice * volume;
    cumulativeVolume += volume;

    const vwap = cumulativeTPV / cumulativeVolume;
    result.push({ time: data[i].time, value: Number(vwap.toFixed(2)) });
  }

  return result;
};

// Support and Resistance Detection
export const findSupportResistance = (
  data: PriceData[],
  lookback: number = 20,
  threshold: number = 0.02 // 2% threshold
): { support: number[]; resistance: number[] } => {
  const support: number[] = [];
  const resistance: number[] = [];

  for (let i = lookback; i < data.length - lookback; i++) {
    const slice = data.slice(i - lookback, i + lookback + 1);
    const current = data[i];

    // Check if current low is a support (lowest in range)
    const isSupport = slice.every((d) => current.low <= d.low * (1 + threshold));
    if (isSupport && !support.includes(current.low)) {
      support.push(Number(current.low.toFixed(2)));
    }

    // Check if current high is a resistance (highest in range)
    const isResistance = slice.every((d) => current.high >= d.high * (1 - threshold));
    if (isResistance && !resistance.includes(current.high)) {
      resistance.push(Number(current.high.toFixed(2)));
    }
  }

  // Remove duplicates and sort
  return {
    support: [...new Set(support)].sort((a, b) => b - a).slice(0, 5),
    resistance: [...new Set(resistance)].sort((a, b) => a - b).slice(0, 5),
  };
};

// Calculate Win Rate from trades
export const calculateWinRate = (trades: any[]): number => {
  if (trades.length === 0) return 0;
  const winningTrades = trades.filter((t) => t.pnl && t.pnl > 0).length;
  return Number(((winningTrades / trades.length) * 100).toFixed(2));
};

// Calculate Sharpe Ratio (simplified)
export const calculateSharpeRatio = (
  returns: number[],
  riskFreeRate: number = 0.02
): number => {
  if (returns.length === 0) return 0;

  const avgReturn = returns.reduce((sum, r) => sum + r, 0) / returns.length;
  const variance = returns.reduce((sum, r) => sum + Math.pow(r - avgReturn, 2), 0) / returns.length;
  const stdDev = Math.sqrt(variance);

  if (stdDev === 0) return 0;

  const excessReturn = avgReturn - riskFreeRate / 252; // Daily risk-free rate
  return Number((excessReturn / stdDev).toFixed(2));
};

// Calculate Maximum Drawdown
export const calculateMaxDrawdown = (equity: number[]): number => {
  if (equity.length === 0) return 0;

  let maxDrawdown = 0;
  let peak = equity[0];

  for (const value of equity) {
    if (value > peak) {
      peak = value;
    }
    const drawdown = ((peak - value) / peak) * 100;
    if (drawdown > maxDrawdown) {
      maxDrawdown = drawdown;
    }
  }

  return Number(maxDrawdown.toFixed(2));
};

// Position Sizing (Kelly Criterion)
export const calculatePositionSize = (
  capital: number,
  winRate: number,
  avgWin: number,
  avgLoss: number
): number => {
  if (avgLoss === 0) return 0;

  const winLossRatio = Math.abs(avgWin / avgLoss);
  const kellyPercent = (winRate - (1 - winRate) / winLossRatio) * 100;

  // Use half-Kelly for safety
  const safeKelly = kellyPercent / 2;

  // Cap at 10% of capital
  const maxPercent = Math.min(Math.max(safeKelly, 0), 10);

  return Number((capital * (maxPercent / 100)).toFixed(2));
};
