import { Injectable, HttpException, HttpStatus, Logger } from '@nestjs/common'
import { InjectModel } from '@nestjs/mongoose'
import { Model } from 'mongoose'
import { User, UserDocument } from './user.schema'
import { PricingService } from '../schemas/pricing.service'
import { GravityTransaction, GravityTransactionType } from '../schemas/gravity-transaction.schema'

const FREE_OPTIMIZE_LIMIT = 3

@Injectable()
export class QuotaService {
  private readonly logger = new Logger(QuotaService.name)

  constructor(
    @InjectModel(User.name) private userModel: Model<UserDocument>,
    @InjectModel(GravityTransaction.name) private gravityTxModel: Model<GravityTransaction>,
    private pricingService: PricingService,
  ) {}

  private async logTransaction(userId: string, amount: number, type: GravityTransactionType, description: string, refId?: string) {
    const u = await this.userModel.findById(userId).select('gravity').exec()
    const balance = u?.gravity ?? 0
    await this.gravityTxModel.create({ userId, amount, balance, type, description, refId })
  }

  /** 仅检查面试引力值是否充足，不扣除（用于先 AI 后扣款模式） */
  async checkInterview(userId: string): Promise<number> {
    const user = await this.userModel.findById(userId).exec()
    if (!user) throw new HttpException('用户不存在', HttpStatus.NOT_FOUND)

    // 迁移旧字段到 gravity
    if ((user.gravity ?? 0) <= 0 && this.hasOldCredits(user)) {
      await this.migrateOldCredits(userId)
    }

    const rates = (await this.pricingService.getConfig()).gravityRates
    const cost = rates.interviewPerUse

    // 检查 gravity 或 shareCredits 是否足够
    const gravOk = (user.gravity ?? 0) >= cost
    const shareOk = (user.shareCredits ?? 0) > 0
    if (!gravOk && !shareOk) {
      throw new HttpException('引力值不足，请充值或分享获取', HttpStatus.FORBIDDEN)
    }
    return cost
  }

  /** 扣除面试引力值（在 AI 调用成功后调用） */
  async deductInterview(userId: string, cost: number) {
    const result = await this.deductGravityOrFallback(userId, cost)
    if (!result) {
      throw new HttpException('引力值不足', HttpStatus.FORBIDDEN)
    }
  }

  /** 检查并扣除面试引力值（所有计划统一走引力值） */
  async checkAndDeductInterview(userId: string) {
    const user = await this.userModel.findById(userId).exec()
    if (!user) throw new HttpException('用户不存在', HttpStatus.NOT_FOUND)

    // 迁移旧字段到 gravity
    if ((user.gravity ?? 0) <= 0 && this.hasOldCredits(user)) {
      await this.migrateOldCredits(userId)
    }

    const rates = (await this.pricingService.getConfig()).gravityRates
    const cost = rates.interviewPerUse

    // 用 gravity 支付，后备 shareCredits
    const result = await this.deductGravityOrFallback(userId, cost)
    if (result) return

    throw new HttpException('引力值不足，请充值或分享获取', HttpStatus.FORBIDDEN)
  }

  /** 检查并扣除优化引力值（所有计划统一走引力值） */
  async checkAndDeductOptimize(userId: string) {
    const user = await this.userModel.findById(userId).exec()
    if (!user) throw new HttpException('用户不存在', HttpStatus.NOT_FOUND)

    // 迁移旧字段
    if ((user.gravity ?? 0) <= 0 && this.hasOldCredits(user)) {
      await this.migrateOldCredits(userId)
    }

    // 免费优化次数
    const freeResult = await this.userModel.findOneAndUpdate(
      { _id: userId, freeOptimizeUsed: { $lt: FREE_OPTIMIZE_LIMIT } },
      { $inc: { freeOptimizeUsed: 1 } },
    ).exec()
    if (freeResult) return

    const rates = (await this.pricingService.getConfig()).gravityRates
    const cost = rates.optimizePerUse

    const result = await this.deductGravityOrFallback(userId, cost)
    if (result) return

    throw new HttpException('引力值不足，请充值或分享获取', HttpStatus.FORBIDDEN)
  }

  /** 检查并扣除下载引力值 */
  async checkAndDeductDownload(userId: string, paidDownload: boolean): Promise<boolean> {
    if (paidDownload) return true

    const rates = (await this.pricingService.getConfig()).gravityRates
    const cost = rates.downloadPerUse

    const result = await this.deductGravityOrFallback(userId, cost)
    if (result) return true

    return false
  }

  /** 从 gravity 扣除，后备从 shareCredits 扣除（兼容旧数据） */
  private async deductGravityOrFallback(userId: string, cost: number): Promise<boolean> {
    const gravResult = await this.userModel.findOneAndUpdate(
      { _id: userId, gravity: { $gte: cost } },
      { $inc: { gravity: -cost } },
    ).exec()
    if (gravResult) {
      await this.logTransaction(userId, -cost, 'interview_deduct', `AI 模拟面试消耗 ${cost} 引力值`)
      return true
    }

    const shareResult = await this.userModel.findOneAndUpdate(
      { _id: userId, shareCredits: { $gt: 0 } },
      { $inc: { shareCredits: -1 } },
    ).exec()
    if (shareResult) {
      await this.logTransaction(userId, 0, 'share_credits_fallback', '分享币后备抵扣 1 次')
      return true
    }

    return false
  }

  /** 增加引力值 */
  async grantGravity(userId: string, amount: number) {
    if (amount <= 0) throw new HttpException('无效数量', HttpStatus.BAD_REQUEST)
    const result = await this.userModel.findByIdAndUpdate(
      userId,
      { $inc: { gravity: amount } },
    ).exec()
    if (!result) throw new HttpException('用户不存在', HttpStatus.NOT_FOUND)
    await this.logTransaction(userId, amount, 'purchase', `充值获得 ${amount} 引力值`)
  }

  /** 设置 VIP 套餐引力值额度 */
  async setPlanQuota(userId: string, gravityAmount: number) {
    const result = await this.userModel.findByIdAndUpdate(userId, {
      $set: {
        gravity: gravityAmount,
        freeOptimizeUsed: FREE_OPTIMIZE_LIMIT,
      },
    }).exec()
    if (!result) throw new HttpException('用户不存在', HttpStatus.NOT_FOUND)
    await this.logTransaction(userId, gravityAmount, 'plan_set', `套餐开通，获得 ${gravityAmount} 引力值`)
  }

  /** 是否为非会员用户授予初始引力值 */
  async grantFreeGravity(userId: string) {
    await this.userModel.findByIdAndUpdate(userId, {
      $set: { interviewCredits: 1, gravity: 5 },
    }).exec()
    await this.logTransaction(userId, 5, 'registration', '注册赠送 5 引力值')
  }

  /** 判断是否有旧额度需要迁移 */
  private hasOldCredits(user: UserDocument): boolean {
    return (user.interviewCredits ?? 0) > 0
      || (user.resumeOptimizeCredits ?? 0) > 0
      || (user.resumeDownloadCredits ?? 0) > 0
      || (user.remaining ?? 0) > 0
  }

  /** 迁移旧额度到 gravity */
  private async migrateOldCredits(userId: string) {
    const user = await this.userModel.findById(userId).exec()
    if (!user) return
    const interviewVal = (user.interviewCredits ?? 0) * 5
    const optimizeVal = (user.resumeOptimizeCredits ?? 0) * 3
    const downloadVal = (user.resumeDownloadCredits ?? 0) * 2
    const oldRemainVal = (user.remaining ?? 0) * 5
    const shareVal = (user.shareCredits ?? 0) * 1
    const total = interviewVal + optimizeVal + downloadVal + oldRemainVal + shareVal
    if (total <= 0) return
    await this.userModel.findByIdAndUpdate(userId, {
      $inc: { gravity: total },
      $set: {
        interviewCredits: 0,
        resumeOptimizeCredits: 0,
        resumeDownloadCredits: 0,
        remaining: 0,
        shareCredits: 0,
      },
    }).exec()
    await this.logTransaction(userId, total, 'migration', `旧额度迁移，获得 ${total} 引力值`)
  }
}
