import { Controller, Post, Get, Param, Body, Query, UseGuards, HttpException, HttpStatus, Logger, Req, HttpCode } from '@nestjs/common'
import { InjectModel } from '@nestjs/mongoose'
import { Model } from 'mongoose'
import { User, UserDocument } from '../user/user.schema'
import { PaymentOrder, PaymentOrderDocument } from '../payment/payment-order.schema'
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'
import { CurrentUser } from '../../common/decorators/current-user.decorator'
import { VirtualPaymentService } from './virtual-payment.service'
import { PricingService } from '../schemas/pricing.service'
import { QuotaService } from '../user/quota.service'
import { Public } from '../../common/decorators/public.decorator'

@Controller('virtual-payment')
export class VirtualPaymentController {
  private readonly logger = new Logger(VirtualPaymentController.name)

  constructor(
    @InjectModel(User.name) private userModel: Model<UserDocument>,
    @InjectModel(PaymentOrder.name) private orderModel: Model<PaymentOrderDocument>,
    private vpService: VirtualPaymentService,
    private pricingService: PricingService,
    private quotaService: QuotaService,
  ) {}

  /**
   * 创建虚拟支付订单（小程序代币充值）
   * 返回前端调起 wx.requestVirtualPayment 所需的全部参数
   */
  @UseGuards(JwtAuthGuard)
  @Post('create')
  @HttpCode(200)
  async create(
    @CurrentUser('userId') userId: string,
    @Body('type') type: string,
    @Body('quantity') quantity: number = 1,
    @Body('wxCode') wxCode: string,
    @Req() req: any,
  ) {
    if (!['interview', 'optimize', 'download', 'growth', 'sprint'].includes(type)) {
      throw new HttpException('无效产品类型', HttpStatus.BAD_REQUEST)
    }

    const user = await this.userModel.findById(userId).exec()
    if (!user) throw new HttpException('用户不存在', HttpStatus.NOT_FOUND)
    if (!user.wxOpenid) {
      throw new HttpException({ message: '未绑定微信', needBindWx: true }, HttpStatus.BAD_REQUEST)
    }
    if (!wxCode) {
      throw new HttpException('缺少 wxCode，请先调用 wx.login()', HttpStatus.BAD_REQUEST)
    }

    const isPlan = type === 'growth' || type === 'sprint'
    if (isPlan && user.plan !== 'free') {
      throw new HttpException('已是会员', HttpStatus.BAD_REQUEST)
    }

    const pricing = await this.pricingService.getConfig()

    let totalFee: number
    let qty = 1
    let productQty = Math.max(1, Math.min(99, quantity || 1))

    if (isPlan) {
      const planCfg = pricing.plans[type]
      if (!planCfg) throw new HttpException('套餐未配置', HttpStatus.INTERNAL_SERVER_ERROR)
      totalFee = planCfg.price
    } else {
      const priceMap: Record<string, number> = {
        interview: pricing.interview.pricePerSession,
        optimize: pricing.resumeOptimize.pricePerOptimize,
        download: pricing.resumeDownload.pricePerDownload,
      }
      qty = productQty
      totalFee = priceMap[type] * qty
      if (!totalFee) throw new HttpException('价格未配置', HttpStatus.INTERNAL_SERVER_ERROR)
    }

    const mode = 'short_series_coin'
    const buyQuantity = totalFee / 100  // 控制台配 1 币 = 1 元，totalFee 单位分
    const outTradeNo = `VP${type.slice(0, 2).toUpperCase()}${Date.now()}${userId.slice(-6)}`
    const env = process.env.VP_SANDBOX === 'true' ? 1 : (process.env.NODE_ENV === 'production' ? 0 : 1)
    const userIp = req.ip || '127.0.0.1'

    // 1. 用 wx.login code 换取 session_key + openid，计算用户态签名
    let openid: string
    let signature: string
    try {
      const signData = this.vpService.buildSignData(outTradeNo, user.wxOpenid, totalFee, userIp, env, mode, buyQuantity)
      const result = await this.vpService.exchangeCodeAndSign(wxCode, signData)
      openid = result.openid
      signature = result.signature
    } catch (e: any) {
      this.logger.error(`[VP] code2session 失败: userId=${userId}, wxCode=${wxCode?.slice(0, 20)}, error=${e.message}, stack=${e.stack?.slice(0, 300)}`)
      throw new HttpException(`微信身份验证失败: ${e.message}`, HttpStatus.BAD_REQUEST)
    }

    // 校验 openid 一致
    this.logger.log(`[VP] code2session 成功: userId=${userId}, wxOpenid=${user.wxOpenid}, code2session_openid=${openid}`)
    if (openid !== user.wxOpenid) {
      this.logger.warn(`[VP] openid 不匹配: userId=${userId}, stored=${user.wxOpenid}, got=${openid}`)
      throw new HttpException('微信身份不匹配', HttpStatus.FORBIDDEN)
    }

    // 2. 计算支付签名 pay_sig
    const signData = this.vpService.buildSignData(outTradeNo, openid, totalFee, userIp, env, mode, buyQuantity)
    const paySig = this.vpService.computePaySig('requestVirtualPayment', signData, env)

    // 3. 创建本地订单
    let title: string
    const titles: Record<string, string> = {
      interview: 'AI 模拟面试',
      optimize: '简历优化',
      download: '简历下载',
      growth: '成长版月度会员',
      sprint: '冲刺版月度会员',
    }
    if (isPlan) {
      title = titles[type]
    } else {
      title = qty > 1 ? `${titles[type]} ×${qty}` : titles[type]
    }
    await this.orderModel.create({
      outTradeNo,
      userId,
      userPhone: user.phone || '',
      amount: totalFee,
      title,
      status: 'pending',
      channel: 'virtual',
      type,
      plan: isPlan ? type : 'growth',
      metadata: { quantity: qty },
    })

    return {
      outTradeNo,
      env,
      mode,
      offerId: this.vpService.getOfferId(),
      signData,
      paySig,
      signature,
      openid,
    }
  }

  /**
   * 微信消息推送回调——虚拟支付通知
   * 在小程序管理后台 → 开发 → 开发管理 → 消息推送 中配置服务器地址指向此接口
   */
  @Public()
  @Post('callback')
  async callback(@Body() body: any, @Req() req: any) {
    try {
      // 微信消息体可能是 XML 或 JSON
      const msg = body.xml || body
      const event = msg.Event || msg.event

      this.logger.log(`[vp-callback] event=${event}, body=${JSON.stringify(body).slice(0, 500)}`)

      if (event === 'xpay_coin_pay_notify') {
        await this.handleCoinPayNotify(msg)
      } else if (event === 'xpay_goods_deliver_notify') {
        await this.handleGoodsDeliverNotify(msg)
      } else if (event === 'xpay_refund_notify') {
        await this.handleRefundNotify(msg)
      } else {
        this.logger.warn(`[vp-callback] 未知事件: ${event}`)
      }

      return { ErrCode: 0, ErrMsg: 'success' }
    } catch (e: any) {
      this.logger.error(`[vp-callback] 处理失败: ${e.message}`)
      return { ErrCode: -1, ErrMsg: e.message }
    }
  }

  private async handleCoinPayNotify(msg: any) {
    const outTradeNo = msg.OutTradeNo || msg.out_trade_no
    if (!outTradeNo) {
      this.logger.warn('[vp-callback] 代币支付通知缺少 outTradeNo')
      return
    }

    const order = await this.orderModel.findOne({ outTradeNo }).exec()
    if (!order) {
      this.logger.warn(`[vp-callback] 订单不存在: ${outTradeNo}`)
      return
    }
    if (order.status !== 'pending') {
      this.logger.log(`[vp-callback] 订单已处理: ${outTradeNo} status=${order.status}`)
      return
    }

    order.status = 'success'
    order.paidAt = new Date()
    order.description = `虚拟支付代币充值成功 env=${msg.Env ?? ''}`
    await order.save()

    const pricing = await this.pricingService.getConfig()

    if (order.type === 'growth' || order.type === 'sprint') {
      // 套餐激活
      const planCfg = pricing.plans[order.type]
      if (!planCfg) return
      const expireAt = new Date()
      expireAt.setDate(expireAt.getDate() + planCfg.durationDays)
      await this.userModel.findByIdAndUpdate(order.userId, {
        $set: {
          plan: order.type,
          [order.type === 'sprint' ? 'sprintExpireAt' : 'vipExpireAt']: expireAt,
          ...(order.type === 'sprint' ? { sprintRemaining: 10 } : {}),
        },
      }).exec()
      await this.quotaService.setPlanQuota(order.userId, planCfg.gravityPerMonth)
      this.logger.log(`[vp-callback] 套餐已激活: userId=${order.userId}, plan=${order.type}, gravityPerMonth=${planCfg.gravityPerMonth}`)
    } else {
      // 发放引力值（按次购买）
      const gravityMap: Record<string, number> = {
        interview: pricing.gravityRates.interviewPerUse,
        optimize: pricing.gravityRates.optimizePerUse,
        download: pricing.gravityRates.downloadPerUse,
      }
      const g = gravityMap[order.type]
      const quantity = order.metadata?.quantity || 1
      if (g) {
        await this.quotaService.grantGravity(order.userId, g * quantity)
        this.logger.log(`[vp-callback] 引力值已发放: userId=${order.userId}, gravity=${g * quantity}`)
      }
    }
  }

  private async handleGoodsDeliverNotify(msg: any) {
    // 道具发货通知——代币模式下通常不需要额外处理
    this.logger.log(`[vp-callback] 道具发货通知: outTradeNo=${msg.OutTradeNo}`)
  }

  private async handleRefundNotify(msg: any) {
    const outTradeNo = msg.MchOrderId || msg.MchOrderNo
    if (!outTradeNo) return
    const order = await this.orderModel.findOne({ outTradeNo }).exec()
    if (!order) return

    order.status = 'refunded'
    order.refundAmount = msg.RefundFee || order.amount
    order.refundedAt = new Date()
    await order.save()
    this.logger.log(`[vp-callback] 订单已退款: ${outTradeNo}`)
  }

  /** 查询本地订单状态（前端轮询） */
  @UseGuards(JwtAuthGuard)
  @Get('check/:outTradeNo')
  async checkOrder(@Param('outTradeNo') outTradeNo: string, @CurrentUser('userId') userId: string) {
    const order = await this.orderModel.findOne({ outTradeNo, userId }).exec()
    if (!order) throw new HttpException('订单不存在', HttpStatus.NOT_FOUND)
    return { status: order.status, type: order.type, paidAt: order.paidAt }
  }
}
