import { Injectable } from '@nestjs/common';
import { MarketService } from '../market/market.service';
import { KLineData } from '../market/interfaces/market.interface';
import {
  MACDResult,
  RSIResult,
  BollingerResult,
  KDJResult,
  SARResult,
  OBVResult,
  DMIResult,
  CCIResult,
  WRResult,
  IndicatorResults,
} from './interfaces/indicator.interface';

@Injectable()
export class IndicatorsService {
  constructor(private readonly marketService: MarketService) {}

  // 计算MACD
  calculateMACD(
    klineData: KLineData[],
    fastPeriod = 12,
    slowPeriod = 26,
    signalPeriod = 9,
  ): MACDResult[] {
    const closes = klineData.map((k) => k.close);
    const ema12 = this.calculateEMA(closes, fastPeriod);
    const ema26 = this.calculateEMA(closes, slowPeriod);

    const dif = ema12.map((v, i) => v - ema26[i]);
    const dea = this.calculateEMA(dif, signalPeriod);
    const macd = dif.map((v, i) => (v - dea[i]) * 2);

    return klineData.map((k, i) => ({
      time: k.time,
      dif: Number(dif[i].toFixed(4)),
      dea: Number(dea[i].toFixed(4)),
      macd: Number(macd[i].toFixed(4)),
    }));
  }

  // 计算RSI
  calculateRSI(klineData: KLineData[], period = 14): RSIResult[] {
    const closes = klineData.map((k) => k.close);
    const rsi = this.calculateRSIValues(closes, period);

    return klineData.map((k, i) => ({
      time: k.time,
      value: Number(rsi[i].toFixed(2)),
      status: this.getRSIStatus(rsi[i]),
    }));
  }

  // 计算布林带
  calculateBollinger(
    klineData: KLineData[],
    period = 20,
    stdDev = 2,
  ): BollingerResult[] {
    const closes = klineData.map((k) => k.close);
    const results: BollingerResult[] = [];

    for (let i = period - 1; i < closes.length; i++) {
      const slice = closes.slice(i - period + 1, i + 1);
      const middle = this.calculateSMA(slice, period);
      const std = this.calculateStdDev(slice, middle);
      const upper = middle + stdDev * std;
      const lower = middle - stdDev * std;
      const currentPrice = closes[i];

      results.push({
        time: klineData[i].time,
        upper: Number(upper.toFixed(2)),
        middle: Number(middle.toFixed(2)),
        lower: Number(lower.toFixed(2)),
        position: currentPrice > upper ? 'upper' : currentPrice < lower ? 'lower' : 'middle',
      });
    }

    return results;
  }

  // 计算KDJ
  calculateKDJ(klineData: KLineData[], n = 9, m1 = 3, m2 = 3): KDJResult[] {
    const highs = klineData.map((k) => k.high);
    const lows = klineData.map((k) => k.low);
    const closes = klineData.map((k) => k.close);

    const rsv: number[] = [];
    const k: number[] = [];
    const d: number[] = [];

    for (let i = 0; i < closes.length; i++) {
      if (i < n - 1) {
        rsv.push(50);
        k.push(50);
        d.push(50);
        continue;
      }

      const periodHighs = highs.slice(i - n + 1, i + 1);
      const periodLows = lows.slice(i - n + 1, i + 1);
      const highest = Math.max(...periodHighs);
      const lowest = Math.min(...periodLows);

      const rsvValue = highest === lowest ? 50 : ((closes[i] - lowest) / (highest - lowest)) * 100;
      rsv.push(rsvValue);

      const kValue = (2 / 3) * (k[i - 1] || 50) + (1 / 3) * rsvValue;
      k.push(kValue);

      const dValue = (2 / 3) * (d[i - 1] || 50) + (1 / 3) * kValue;
      d.push(dValue);
    }

    const j = k.map((kv, i) => 3 * kv - 2 * d[i]);

    return klineData.map((kline, i) => ({
      time: kline.time,
      k: Number(k[i].toFixed(2)),
      d: Number(d[i].toFixed(2)),
      j: Number(j[i].toFixed(2)),
      signal: this.getKDJSignal(k[i], d[i], k[i - 1], d[i - 1]),
    }));
  }

  // 计算SAR (抛物线转向)
  calculateSAR(klineData: KLineData[], af = 0.02, maxAf = 0.2): SARResult[] {
    const highs = klineData.map((k) => k.high);
    const lows = klineData.map((k) => k.low);
    const results: SARResult[] = [];

    let isLong = true;
    let sar = lows[0];
    let ep = highs[0];
    let afValue = af;

    for (let i = 1; i < klineData.length; i++) {
      sar = sar + afValue * (ep - sar);

      if (isLong) {
        if (lows[i] < sar) {
          isLong = false;
          sar = ep;
          ep = lows[i];
          afValue = af;
        } else {
          if (highs[i] > ep) {
            ep = highs[i];
            afValue = Math.min(afValue + af, maxAf);
          }
        }
      } else {
        if (highs[i] > sar) {
          isLong = true;
          sar = ep;
          ep = highs[i];
          afValue = af;
        } else {
          if (lows[i] < ep) {
            ep = lows[i];
            afValue = Math.min(afValue + af, maxAf);
          }
        }
      }

      results.push({
        time: klineData[i].time,
        value: Number(sar.toFixed(2)),
        isLong,
      });
    }

    return results;
  }

  // 计算OBV
  calculateOBV(klineData: KLineData[]): OBVResult[] {
    const obv: number[] = [0];

    for (let i = 1; i < klineData.length; i++) {
      const prevClose = klineData[i - 1].close;
      const currentClose = klineData[i].close;
      const volume = klineData[i].volume;

      if (currentClose > prevClose) {
        obv.push(obv[i - 1] + volume);
      } else if (currentClose < prevClose) {
        obv.push(obv[i - 1] - volume);
      } else {
        obv.push(obv[i - 1]);
      }
    }

    return klineData.map((k, i) => ({
      time: k.time,
      value: obv[i],
    }));
  }

  // 计算DMI
  calculateDMI(klineData: KLineData[], period = 14): DMIResult[] {
    const highs = klineData.map((k) => k.high);
    const lows = klineData.map((k) => k.low);
    const closes = klineData.map((k) => k.close);

    const tr: number[] = [];
    const plusDM: number[] = [];
    const minusDM: number[] = [];

    for (let i = 1; i < klineData.length; i++) {
      const highDiff = highs[i] - highs[i - 1];
      const lowDiff = lows[i - 1] - lows[i];

      tr.push(Math.max(
        highs[i] - lows[i],
        Math.abs(highs[i] - closes[i - 1]),
        Math.abs(lows[i] - closes[i - 1]),
      ));

      plusDM.push(highDiff > lowDiff && highDiff > 0 ? highDiff : 0);
      minusDM.push(lowDiff > highDiff && lowDiff > 0 ? lowDiff : 0);
    }

    const atr = this.calculateEMA(tr, period);
    const plusDI = this.calculateEMA(plusDM, period).map((v, i) => (v / atr[i]) * 100);
    const minusDI = this.calculateEMA(minusDM, period).map((v, i) => (v / atr[i]) * 100);
    const dx = plusDI.map((v, i) => Math.abs(v - minusDI[i]) / (v + minusDI[i]) * 100);
    const adx = this.calculateEMA(dx, period);

    return klineData.slice(1).map((k, i) => ({
      time: k.time,
      plusDI: Number(plusDI[i].toFixed(2)),
      minusDI: Number(minusDI[i].toFixed(2)),
      adx: Number(adx[i].toFixed(2)),
    }));
  }

  // 计算CCI
  calculateCCI(klineData: KLineData[], period = 14): CCIResult[] {
    const results: CCIResult[] = [];

    for (let i = period - 1; i < klineData.length; i++) {
      const tp = klineData.slice(i - period + 1, i + 1).map(
        (k) => (k.high + k.low + k.close) / 3,
      );
      const smaTP = tp.reduce((a, b) => a + b, 0) / period;
      const meanDeviation = tp.reduce((a, b) => a + Math.abs(b - smaTP), 0) / period;
      const cci = (tp[tp.length - 1] - smaTP) / (0.015 * meanDeviation);

      results.push({
        time: klineData[i].time,
        value: Number(cci.toFixed(2)),
      });
    }

    return results;
  }

  // 计算WR (威廉指标)
  calculateWR(klineData: KLineData[], period = 14): WRResult[] {
    const results: WRResult[] = [];

    for (let i = period - 1; i < klineData.length; i++) {
      const periodData = klineData.slice(i - period + 1, i + 1);
      const highest = Math.max(...periodData.map((k) => k.high));
      const lowest = Math.min(...periodData.map((k) => k.low));
      const close = klineData[i].close;

      const wr = highest === lowest ? -50 : ((highest - close) / (highest - lowest)) * -100;

      results.push({
        time: klineData[i].time,
        value: Number(wr.toFixed(2)),
      });
    }

    return results;
  }

  // 计算移动平均线
  calculateMA(klineData: KLineData[], period: number): { time: string; value: number }[] {
    const closes = klineData.map((k) => k.close);
    const ma = this.calculateSMAArray(closes, period);

    return klineData.slice(period - 1).map((k, i) => ({
      time: k.time,
      value: Number(ma[i].toFixed(2)),
    }));
  }

  // 获取所有指标
  async calculateAllIndicators(
    symbol: string,
    period: string,
  ): Promise<IndicatorResults> {
    const klineData = await this.marketService.getKlineData(symbol, period, 200);

    return {
      macd: this.calculateMACD(klineData),
      rsi: this.calculateRSI(klineData),
      bollinger: this.calculateBollinger(klineData),
      kdj: this.calculateKDJ(klineData),
      sar: this.calculateSAR(klineData),
      obv: this.calculateOBV(klineData),
      dmi: this.calculateDMI(klineData),
      cci: this.calculateCCI(klineData),
      wr: this.calculateWR(klineData),
      ma5: this.calculateMA(klineData, 5),
      ma10: this.calculateMA(klineData, 10),
      ma20: this.calculateMA(klineData, 20),
      ma60: this.calculateMA(klineData, 60),
    };
  }

  // 辅助方法
  private calculateEMA(data: number[], period: number): number[] {
    const k = 2 / (period + 1);
    const ema: number[] = [data[0]];

    for (let i = 1; i < data.length; i++) {
      ema.push(data[i] * k + ema[i - 1] * (1 - k));
    }

    return ema;
  }

  private calculateSMA(data: number[], period: number): number {
    const sum = data.slice(-period).reduce((a, b) => a + b, 0);
    return sum / period;
  }

  private calculateSMAArray(data: number[], period: number): number[] {
    const sma: number[] = [];
    for (let i = period - 1; i < data.length; i++) {
      const sum = data.slice(i - period + 1, i + 1).reduce((a, b) => a + b, 0);
      sma.push(sum / period);
    }
    return sma;
  }

  private calculateStdDev(data: number[], mean: number): number {
    const squareDiffs = data.map((value) => Math.pow(value - mean, 2));
    const avgSquareDiff = squareDiffs.reduce((a, b) => a + b, 0) / data.length;
    return Math.sqrt(avgSquareDiff);
  }

  private calculateRSIValues(closes: number[], period: number): number[] {
    const gains: number[] = [];
    const losses: number[] = [];

    for (let i = 1; i < closes.length; i++) {
      const change = closes[i] - closes[i - 1];
      gains.push(change > 0 ? change : 0);
      losses.push(change < 0 ? Math.abs(change) : 0);
    }

    const rsi: number[] = [];
    let avgGain = gains.slice(0, period).reduce((a, b) => a + b, 0) / period;
    let avgLoss = losses.slice(0, period).reduce((a, b) => a + b, 0) / period;

    for (let i = period; i < gains.length; i++) {
      avgGain = (avgGain * (period - 1) + gains[i]) / period;
      avgLoss = (avgLoss * (period - 1) + losses[i]) / period;

      const rs = avgGain / avgLoss;
      rsi.push(100 - 100 / (1 + rs));
    }

    // 填充前面的数据
    return [...Array(period).fill(50), ...rsi];
  }

  private getRSIStatus(rsi: number): 'overbought' | 'oversold' | 'normal' {
    if (rsi > 70) return 'overbought';
    if (rsi < 30) return 'oversold';
    return 'normal';
  }

  private getKDJSignal(
    k: number,
    d: number,
    prevK: number,
    prevD: number,
  ): 'golden_cross' | 'dead_cross' | 'neutral' {
    if (prevK < prevD && k > d) return 'golden_cross';
    if (prevK > prevD && k < d) return 'dead_cross';
    return 'neutral';
  }
}
