import { Injectable } from '@nestjs/common';
import { MarketService } from '../market/market.service';
import {
  OptionPricingResult,
  OptionContract,
  OptionChain,
  VolatilitySurface,
  StrategyPayoff,
} from './interfaces/options.interface';

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

  /**
   * Black-Scholes期权定价模型
   */
  calculateBlackScholes(
    S: number,        // 标的资产价格
    K: number,        // 行权价
    T: number,        // 到期时间（年）
    r: number,        // 无风险利率
    sigma: number,    // 波动率
    type: 'call' | 'put',
  ): OptionPricingResult {
    const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T));
    const d2 = d1 - sigma * Math.sqrt(T);

    const nd1 = this.cumulativeNormalDistribution(d1);
    const nd2 = this.cumulativeNormalDistribution(d2);
    const nPd1 = this.normalProbabilityDensity(d1);

    let price: number;
    let delta: number;

    if (type === 'call') {
      price = S * nd1 - K * Math.exp(-r * T) * nd2;
      delta = nd1;
    } else {
      price = K * Math.exp(-r * T) * (1 - nd2) - S * (1 - nd1);
      delta = nd1 - 1;
    }

    const gamma = nPd1 / (S * sigma * Math.sqrt(T));
    const theta = -(S * nPd1 * sigma) / (2 * Math.sqrt(T)) 
      - r * K * Math.exp(-r * T) * (type === 'call' ? nd2 : 1 - nd2);
    const vega = S * Math.sqrt(T) * nPd1;
    const rho = type === 'call' 
      ? K * T * Math.exp(-r * T) * nd2 
      : -K * T * Math.exp(-r * T) * (1 - nd2);

    return {
      price: Number(price.toFixed(4)),
      delta: Number(delta.toFixed(4)),
      gamma: Number(gamma.toFixed(4)),
      theta: Number((theta / 365).toFixed(4)), // 转换为日值
      vega: Number((vega / 100).toFixed(4)),   // 转换为1%波动率变化
      rho: Number((rho / 100).toFixed(4)),     // 转换为1%利率变化
    };
  }

  /**
   * 计算隐含波动率 (使用牛顿迭代法)
   */
  calculateImpliedVolatility(
    S: number,
    K: number,
    T: number,
    r: number,
    marketPrice: number,
    type: 'call' | 'put',
  ): number {
    let sigma = 0.3; // 初始猜测值
    const tolerance = 0.0001;
    const maxIterations = 100;

    for (let i = 0; i < maxIterations; i++) {
      const result = this.calculateBlackScholes(S, K, T, r, sigma, type);
      const priceDiff = result.price - marketPrice;

      if (Math.abs(priceDiff) < tolerance) {
        return Number(sigma.toFixed(4));
      }

      // 使用vega作为导数
      const vega = result.vega * 100; // 转回原始值
      if (vega === 0) break;

      sigma = sigma - priceDiff / vega;
      
      // 限制波动率范围
      if (sigma <= 0) sigma = 0.01;
      if (sigma > 5) sigma = 5;
    }

    return Number(sigma.toFixed(4));
  }

  /**
   * 获取期权链
   */
  async getOptionChain(underlying: string, expiry?: string): Promise<OptionChain> {
    const underlyingData = await this.marketService.getTickData(underlying);
    const underlyingPrice = underlyingData.price;

    // 如果没有指定到期日，使用默认的（30天后）
    const expiryDate = expiry || this.getDefaultExpiryDate();
    
    // 生成行权价列表（基于标的资产价格上下波动20%）
    const strikes = this.generateStrikes(underlyingPrice);

    const T = this.calculateTimeToExpiry(expiryDate);
    const r = 0.025; // 默认无风险利率 2.5%
    const sigma = 0.25; // 默认波动率 25%

    const calls: OptionContract[] = [];
    const puts: OptionContract[] = [];

    for (const strike of strikes) {
      const callResult = this.calculateBlackScholes(
        underlyingPrice, strike, T, r, sigma, 'call',
      );
      const putResult = this.calculateBlackScholes(
        underlyingPrice, strike, T, r, sigma, 'put',
      );

      calls.push({
        symbol: `${underlying}${expiryDate.replace(/-/g, '')}C${strike}`,
        underlying,
        type: 'call',
        strikePrice: strike,
        expiryDate,
        ...callResult,
        iv: sigma,
        volume: Math.floor(Math.random() * 1000),
        openInterest: Math.floor(Math.random() * 5000),
      });

      puts.push({
        symbol: `${underlying}${expiryDate.replace(/-/g, '')}P${strike}`,
        underlying,
        type: 'put',
        strikePrice: strike,
        expiryDate,
        ...putResult,
        iv: sigma,
        volume: Math.floor(Math.random() * 1000),
        openInterest: Math.floor(Math.random() * 5000),
      });
    }

    return {
      underlying,
      underlyingPrice,
      expiryDate,
      strikes,
      calls,
      puts,
    };
  }

  /**
   * 计算期权策略盈亏
   */
  calculateStrategyPayoff(
    underlyingPrice: number,
    strikes: number[],
    optionPrices: number[],
    quantities: number[],
    types: ('call' | 'put')[],
  ): StrategyPayoff {
    const priceRange = 0.3; // 价格范围 ±30%
    const steps = 50;
    const minPrice = underlyingPrice * (1 - priceRange);
    const maxPrice = underlyingPrice * (1 + priceRange);
    const stepSize = (maxPrice - minPrice) / steps;

    const pricePoints: number[] = [];
    const payoffs: number[] = [];

    for (let i = 0; i <= steps; i++) {
      const price = minPrice + i * stepSize;
      pricePoints.push(Number(price.toFixed(2)));

      let payoff = 0;
      for (let j = 0; j < strikes.length; j++) {
        const intrinsicValue = this.calculateIntrinsicValue(
          price, strikes[j], types[j],
        );
        payoff += (intrinsicValue - optionPrices[j]) * quantities[j];
      }
      payoffs.push(Number(payoff.toFixed(2)));
    }

    const maxProfit = Math.max(...payoffs);
    const maxLoss = Math.min(...payoffs);
    const breakeven = this.calculateBreakevenPoints(pricePoints, payoffs);

    return {
      pricePoints,
      payoffs,
      maxProfit: maxProfit > 0 ? maxProfit : Infinity,
      maxLoss: maxLoss < 0 ? maxLoss : -Infinity,
      breakeven,
    };
  }

  /**
   * 获取波动率曲面
   */
  async getVolatilitySurface(underlying: string): Promise<VolatilitySurface> {
    const underlyingData = await this.marketService.getTickData(underlying);
    const price = underlyingData.price;
    
    const expiries = [
      this.getExpiryDate(30),
      this.getExpiryDate(60),
      this.getExpiryDate(90),
      this.getExpiryDate(180),
    ];

    const points: any[] = [];
    
    for (const expiry of expiries) {
      const strikes = this.generateStrikes(price, 0.15);
      const T = this.calculateTimeToExpiry(expiry);
      
      for (const strike of strikes) {
        // 模拟隐含波动率（实际应从市场数据获取）
        const moneyness = strike / price;
        const baseIV = 0.25;
        const skew = (moneyness - 1) * 0.1; // 波动率偏斜
        const iv = baseIV + skew + (Math.random() - 0.5) * 0.02;
        
        points.push({
          strike,
          expiry,
          iv: Number(iv.toFixed(4)),
        });
      }
    }

    return {
      underlying,
      points,
    };
  }

  // 辅助方法
  private cumulativeNormalDistribution(x: number): number {
    const t = 1 / (1 + 0.2316419 * Math.abs(x));
    const d = 0.3989423 * Math.exp((-x * x) / 2);
    const prob = 
      d * t * 
      (0.3193815 + 
        t * (-0.3565638 + 
          t * (1.781478 + 
            t * (-1.821256 + t * 1.330274))));
    
    if (x > 0) {
      return 1 - prob;
    }
    return prob;
  }

  private normalProbabilityDensity(x: number): number {
    return Math.exp((-x * x) / 2) / Math.sqrt(2 * Math.PI);
  }

  private getDefaultExpiryDate(): string {
    const date = new Date();
    date.setDate(date.getDate() + 30);
    return date.toISOString().split('T')[0];
  }

  private getExpiryDate(daysFromNow: number): string {
    const date = new Date();
    date.setDate(date.getDate() + daysFromNow);
    return date.toISOString().split('T')[0];
  }

  private generateStrikes(underlyingPrice: number, range = 0.2): number[] {
    const strikes: number[] = [];
    const step = underlyingPrice * 0.05; // 5% 间隔
    const count = Math.floor((underlyingPrice * range) / step);

    for (let i = -count; i <= count; i++) {
      const strike = underlyingPrice + i * step;
      // 四舍五入到合理精度
      const roundedStrike = underlyingPrice > 1000 
        ? Math.round(strike / 100) * 100
        : underlyingPrice > 100
          ? Math.round(strike / 10) * 10
          : Math.round(strike);
      strikes.push(roundedStrike);
    }

    return strikes.sort((a, b) => a - b);
  }

  private calculateTimeToExpiry(expiryDate: string): number {
    const expiry = new Date(expiryDate);
    const now = new Date();
    const diffTime = expiry.getTime() - now.getTime();
    const diffDays = diffTime / (1000 * 60 * 60 * 24);
    return Math.max(diffDays / 365, 0.001); // 最小0.001年
  }

  private calculateIntrinsicValue(
    underlyingPrice: number,
    strike: number,
    type: 'call' | 'put',
  ): number {
    if (type === 'call') {
      return Math.max(0, underlyingPrice - strike);
    } else {
      return Math.max(0, strike - underlyingPrice);
    }
  }

  private calculateBreakevenPoints(pricePoints: number[], payoffs: number[]): number[] {
    const breakeven: number[] = [];
    
    for (let i = 1; i < payoffs.length; i++) {
      if ((payoffs[i - 1] < 0 && payoffs[i] >= 0) || 
          (payoffs[i - 1] > 0 && payoffs[i] <= 0)) {
        // 线性插值
        const ratio = Math.abs(payoffs[i - 1]) / 
          (Math.abs(payoffs[i - 1]) + Math.abs(payoffs[i]));
        const breakevenPrice = pricePoints[i - 1] + 
          ratio * (pricePoints[i] - pricePoints[i - 1]);
        breakeven.push(Number(breakevenPrice.toFixed(2)));
      }
    }

    return breakeven;
  }
}
