import { Controller, Get, Post, Body, Param, Query } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiParam, ApiResponse } from '@nestjs/swagger';
import { OptionsService } from './options.service';
import { OptionPricingDto, GetOptionChainDto, CalculateStrategyDto } from './dto/options.dto';

@ApiTags('期权分析')
@Controller('options')
export class OptionsController {
  constructor(private readonly optionsService: OptionsService) {}

  @Post('pricing')
  @ApiOperation({ summary: '期权定价计算 (Black-Scholes模型)' })
  @ApiResponse({ status: 200, description: '计算成功' })
  async calculatePricing(@Body() dto: OptionPricingDto) {
    const underlyingPrice = dto.underlyingPrice || 
      await this.optionsService['marketService'].getTickData(dto.underlying)
        .then(t => t.price);
    
    const T = this.calculateTimeToExpiry(dto.expiry);
    const r = dto.riskFreeRate || 0.025;
    const sigma = dto.volatility || 0.25;

    return this.optionsService.calculateBlackScholes(
      underlyingPrice,
      dto.strike,
      T,
      r,
      sigma,
      dto.type,
    );
  }

  @Post('implied-vol')
  @ApiOperation({ summary: '计算隐含波动率' })
  @ApiResponse({ status: 200, description: '计算成功' })
  async calculateIV(
    @Body() dto: OptionPricingDto & { marketPrice: number },
  ) {
    const underlyingPrice = dto.underlyingPrice || 
      await this.optionsService['marketService'].getTickData(dto.underlying)
        .then(t => t.price);
    
    const T = this.calculateTimeToExpiry(dto.expiry);
    const r = dto.riskFreeRate || 0.025;

    const iv = this.optionsService.calculateImpliedVolatility(
      underlyingPrice,
      dto.strike,
      T,
      r,
      dto.marketPrice,
      dto.type,
    );

    return { impliedVolatility: iv };
  }

  @Get('chain/:underlying')
  @ApiOperation({ summary: '获取期权链' })
  @ApiParam({ name: 'underlying', description: '标的资产代码' })
  @ApiResponse({ status: 200, description: '获取成功' })
  async getOptionChain(
    @Param('underlying') underlying: string,
    @Query() dto: GetOptionChainDto,
  ) {
    return this.optionsService.getOptionChain(underlying, dto.expiry);
  }

  @Get('volatility-surface/:underlying')
  @ApiOperation({ summary: '获取波动率曲面' })
  @ApiParam({ name: 'underlying', description: '标的资产代码' })
  @ApiResponse({ status: 200, description: '获取成功' })
  async getVolatilitySurface(@Param('underlying') underlying: string) {
    return this.optionsService.getVolatilitySurface(underlying);
  }

  @Post('strategy/payoff')
  @ApiOperation({ summary: '计算期权策略盈亏' })
  @ApiResponse({ status: 200, description: '计算成功' })
  async calculateStrategyPayoff(@Body() dto: {
    underlying: string;
    strategy: string;
    legs: Array<{
      type: 'call' | 'put';
      strike: number;
      position: 1 | -1;
    }>;
    expiry: string;
  }) {
    const underlyingData = await this.optionsService['marketService']
      .getTickData(dto.underlying);
    const underlyingPrice = underlyingData.price;
    
    const T = this.calculateTimeToExpiry(dto.expiry);
    const r = 0.025;
    const sigma = 0.25;

    const strikes: number[] = [];
    const optionPrices: number[] = [];
    const quantities: number[] = [];
    const types: ('call' | 'put')[] = [];

    for (const leg of dto.legs) {
      const price = this.optionsService.calculateBlackScholes(
        underlyingPrice, leg.strike, T, r, sigma, leg.type,
      ).price;

      strikes.push(leg.strike);
      optionPrices.push(price);
      quantities.push(leg.position);
      types.push(leg.type);
    }

    return this.optionsService.calculateStrategyPayoff(
      underlyingPrice,
      strikes,
      optionPrices,
      quantities,
      types,
    );
  }

  @Get('strategies')
  @ApiOperation({ summary: '获取常用期权策略' })
  @ApiResponse({ status: 200, description: '获取成功' })
  getCommonStrategies() {
    return [
      {
        id: 'long_call',
        name: '买入看涨期权',
        description: '看涨策略，风险有限，收益无限',
        legs: [{ type: 'call', position: 1 }],
      },
      {
        id: 'long_put',
        name: '买入看跌期权',
        description: '看跌策略，风险有限，收益有限',
        legs: [{ type: 'put', position: 1 }],
      },
      {
        id: 'covered_call',
        name: '备兑看涨',
        description: '持有标的资产同时卖出看涨期权',
        legs: [{ type: 'call', position: -1 }],
      },
      {
        id: 'protective_put',
        name: '保护性看跌',
        description: '持有标的资产同时买入看跌期权',
        legs: [{ type: 'put', position: 1 }],
      },
      {
        id: 'bull_call_spread',
        name: '牛市看涨价差',
        description: '买入低行权价看涨，卖出高行权价看涨',
        legs: [
          { type: 'call', position: 1 },
          { type: 'call', position: -1 },
        ],
      },
      {
        id: 'bear_put_spread',
        name: '熊市看跌价差',
        description: '买入高行权价看跌，卖出低行权价看跌',
        legs: [
          { type: 'put', position: 1 },
          { type: 'put', position: -1 },
        ],
      },
      {
        id: 'long_straddle',
        name: '买入跨式',
        description: '同时买入相同行权价的看涨和看跌',
        legs: [
          { type: 'call', position: 1 },
          { type: 'put', position: 1 },
        ],
      },
      {
        id: 'short_strangle',
        name: '卖出宽跨式',
        description: '卖出不同行权价的看涨和看跌',
        legs: [
          { type: 'call', position: -1 },
          { type: 'put', position: -1 },
        ],
      },
      {
        id: 'iron_condor',
        name: '铁鹰式',
        description: '卖出内侧期权，买入外侧期权',
        legs: [
          { type: 'call', position: -1 },
          { type: 'call', position: 1 },
          { type: 'put', position: -1 },
          { type: 'put', position: 1 },
        ],
      },
      {
        id: 'butterfly',
        name: '蝶式价差',
        description: '买入低高价期权，卖出2份中间价期权',
        legs: [
          { type: 'call', position: 1 },
          { type: 'call', position: -2 },
          { type: 'call', position: 1 },
        ],
      },
    ];
  }

  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);
  }
}
