import type { CandlestickData, LineData } from '../types';

// Simple Moving Average
export function calculateSMA(data: CandlestickData[], period: number): LineData[] {
  const result: LineData[] = [];
  
  for (let i = period - 1; i < data.length; i++) {
    let sum = 0;
    for (let j = 0; j < period; j++) {
      sum += data[i - j].close;
    }
    result.push({
      time: data[i].time,
      value: parseFloat((sum / period).toFixed(2)),
    });
  }
  
  return result;
}

// Exponential Moving Average
export function calculateEMA(data: CandlestickData[], period: number): LineData[] {
  const result: LineData[] = [];
  const multiplier = 2 / (period + 1);
  
  // Start with SMA for the first period
  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: parseFloat(ema.toFixed(2)),
  });
  
  // Calculate EMA for the rest
  for (let i = period; i < data.length; i++) {
    ema = (data[i].close - ema) * multiplier + ema;
    result.push({
      time: data[i].time,
      value: parseFloat(ema.toFixed(2)),
    });
  }
  
  return result;
}

// Bollinger Bands
export function calculateBollingerBands(data: CandlestickData[], period: number = 20, stdDev: number = 2) {
  const sma = calculateSMA(data, period);
  const upper: LineData[] = [];
  const lower: LineData[] = [];
  
  for (let i = period - 1; i < data.length; i++) {
    let sumSquaredDiff = 0;
    const smaValue = sma[i - (period - 1)].value;
    
    for (let j = 0; j < period; j++) {
      const diff = data[i - j].close - smaValue;
      sumSquaredDiff += diff * diff;
    }
    
    const standardDeviation = Math.sqrt(sumSquaredDiff / period);
    const upperBand = smaValue + (standardDeviation * stdDev);
    const lowerBand = smaValue - (standardDeviation * stdDev);
    
    upper.push({
      time: data[i].time,
      value: parseFloat(upperBand.toFixed(2)),
    });
    
    lower.push({
      time: data[i].time,
      value: parseFloat(lowerBand.toFixed(2)),
    });
  }
  
  return { upper, middle: sma, lower };
}

// Relative Strength Index (RSI)
export function calculateRSI(data: CandlestickData[], period: number = 14): LineData[] {
  const result: LineData[] = [];
  
  if (data.length < period + 1) return result;
  
  // Calculate initial average gain/loss
  let avgGain = 0;
  let avgLoss = 0;
  
  for (let i = 1; i <= period; i++) {
    const change = data[i].close - data[i - 1].close;
    if (change > 0) {
      avgGain += change;
    } else {
      avgLoss += Math.abs(change);
    }
  }
  
  avgGain /= period;
  avgLoss /= period;
  
  // Calculate RSI for each 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: parseFloat(rsi.toFixed(2)),
    });
  }
  
  return result;
}

// MACD (Moving Average Convergence Divergence)
export function calculateMACD(
  data: CandlestickData[], 
  fastPeriod: number = 12, 
  slowPeriod: number = 26, 
  signalPeriod: number = 9
) {
  const emaFast = calculateEMA(data, fastPeriod);
  const emaSlow = calculateEMA(data, slowPeriod);
  
  const macdLine: LineData[] = [];
  const macdData: number[] = [];
  
  // Calculate MACD line
  for (let i = 0; i < Math.min(emaFast.length, emaSlow.length); i++) {
    const fastIdx = emaFast.findIndex(d => d.time === emaSlow[i].time);
    if (fastIdx !== -1) {
      const macdValue = emaFast[fastIdx].value - emaSlow[i].value;
      macdLine.push({
        time: emaSlow[i].time,
        value: parseFloat(macdValue.toFixed(2)),
      });
      macdData.push(macdValue);
    }
  }
  
  // Calculate signal line (EMA of MACD)
  const signalLine: LineData[] = [];
  if (macdData.length >= signalPeriod) {
    const multiplier = 2 / (signalPeriod + 1);
    let ema = macdData.slice(0, signalPeriod).reduce((a, b) => a + b, 0) / signalPeriod;
    
    signalLine.push({
      time: macdLine[signalPeriod - 1].time,
      value: parseFloat(ema.toFixed(2)),
    });
    
    for (let i = signalPeriod; i < macdData.length; i++) {
      ema = (macdData[i] - ema) * multiplier + ema;
      signalLine.push({
        time: macdLine[i].time,
        value: parseFloat(ema.toFixed(2)),
      });
    }
  }
  
  // Calculate histogram
  const histogram: LineData[] = [];
  for (let i = 0; i < signalLine.length; i++) {
    const macdIdx = macdLine.findIndex(d => d.time === signalLine[i].time);
    if (macdIdx !== -1) {
      histogram.push({
        time: signalLine[i].time,
        value: parseFloat((macdLine[macdIdx].value - signalLine[i].value).toFixed(2)),
      });
    }
  }
  
  return { macdLine, signalLine, histogram };
}

// Volume Weighted Average Price (VWAP)
export function calculateVWAP(data: CandlestickData[]): LineData[] {
  const result: LineData[] = [];
  let cumulativeTPV = 0; // Cumulative 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 || 0;
    
    cumulativeTPV += typicalPrice * volume;
    cumulativeVolume += volume;
    
    if (cumulativeVolume > 0) {
      result.push({
        time: data[i].time,
        value: parseFloat((cumulativeTPV / cumulativeVolume).toFixed(2)),
      });
    }
  }
  
  return result;
}

// Stochastic Oscillator
export function calculateStochastic(data: CandlestickData[], period: number = 14, smoothK: number = 3, smoothD: number = 3) {
  const kValues: LineData[] = [];
  const dValues: LineData[] = [];
  
  // Calculate %K
  for (let i = period - 1; i < data.length; i++) {
    let lowestLow = data[i].low;
    let highestHigh = data[i].high;
    
    for (let j = 1; j < period; j++) {
      lowestLow = Math.min(lowestLow, data[i - j].low);
      highestHigh = Math.max(highestHigh, data[i - j].high);
    }
    
    const k = ((data[i].close - lowestLow) / (highestHigh - lowestLow)) * 100;
    kValues.push({
      time: data[i].time,
      value: parseFloat(k.toFixed(2)),
    });
  }
  
  // Smooth %K if needed
  const smoothedK = smoothK > 1 ? calculateSMAFromLineData(kValues, smoothK) : kValues;
  
  // Calculate %D (SMA of %K)
  const smoothedD = calculateSMAFromLineData(smoothedK, smoothD);
  
  return { k: smoothedK, d: smoothedD };
}

// Helper function to calculate SMA from LineData
function calculateSMAFromLineData(data: LineData[], period: number): LineData[] {
  const result: LineData[] = [];
  
  for (let i = period - 1; i < data.length; i++) {
    let sum = 0;
    for (let j = 0; j < period; j++) {
      sum += data[i - j].value;
    }
    result.push({
      time: data[i].time,
      value: parseFloat((sum / period).toFixed(2)),
    });
  }
  
  return result;
}