import { DataSource, EntityManager, Repository } from 'typeorm';
import { CreditBalanceMt, CreditBalanceLogMt, CreditBalanceChangeType } from '../entities';

export interface SetLimitParams {
  tenantId: number;
  userId: number;
  totalLimit: number;
  operatorId?: number;
  remark?: string;
}

export interface AdjustLimitParams {
  tenantId: number;
  userId: number;
  adjustAmount: number;
  operatorId?: number;
  remark?: string;
}

export interface DeductAmountParams {
  tenantId: number;
  userId: number;
  amount: number;
  referenceId?: string;
  operatorId?: number;
  remark?: string;
}

export interface RestoreAmountParams {
  tenantId: number;
  userId: number;
  amount: number;
  changeType: CreditBalanceChangeType;
  referenceId?: string;
  operatorId?: number;
  remark?: string;
}

export class CreditBalanceService {
  private creditBalanceRepository: Repository<CreditBalanceMt>;
  private creditBalanceLogRepository: Repository<CreditBalanceLogMt>;

  constructor(private dataSource: DataSource) {
    this.creditBalanceRepository = dataSource.getRepository(CreditBalanceMt);
    this.creditBalanceLogRepository = dataSource.getRepository(CreditBalanceLogMt);
  }

  /**
   * 设置用户信用额度
   */
  async setLimit(params: SetLimitParams): Promise<CreditBalanceMt> {
    const { tenantId, userId, totalLimit, operatorId, remark } = params;

    return await this.dataSource.transaction(async (manager) => {
      const repository = manager.getRepository(CreditBalanceMt);
      const logRepository = manager.getRepository(CreditBalanceLogMt);

      // 查找或创建信用额度记录
      let creditBalance = await repository.findOne({
        where: { tenantId, userId }
      });

      const beforeTotal = creditBalance ? Number(creditBalance.totalLimit) : 0;
      const beforeUsed = creditBalance ? Number(creditBalance.usedAmount) : 0;

      if (creditBalance) {
        // 更新现有记录
        creditBalance.totalLimit = totalLimit;
        creditBalance.updatedAt = new Date();
        await repository.save(creditBalance);
      } else {
        // 创建新记录
        creditBalance = repository.create({
          tenantId,
          userId,
          totalLimit,
          usedAmount: 0,
          isEnabled: 1
        });
        await repository.save(creditBalance);
      }

      // 记录变更日志
      const changeAmount = totalLimit - beforeTotal;
      await this.createBalanceLog({
        manager,
        logRepository,
        tenantId,
        userId,
        changeType: CreditBalanceChangeType.SET_LIMIT,
        changeAmount,
        beforeTotal,
        afterTotal: totalLimit,
        beforeUsed,
        afterUsed: beforeUsed,
        referenceId: null,
        operatorId,
        remark
      });

      return creditBalance;
    });
  }

  /**
   * 调整用户信用额度
   */
  async adjustLimit(params: AdjustLimitParams): Promise<CreditBalanceMt> {
    const { tenantId, userId, adjustAmount, operatorId, remark } = params;

    if (adjustAmount === 0) {
      throw new Error('调整金额不能为0');
    }

    return await this.dataSource.transaction(async (manager) => {
      const repository = manager.getRepository(CreditBalanceMt);
      const logRepository = manager.getRepository(CreditBalanceLogMt);

      // 查找信用额度记录
      const creditBalance = await repository.findOne({
        where: { tenantId, userId }
      });

      if (!creditBalance) {
        throw new Error('用户信用额度记录不存在');
      }

      const beforeTotal = Number(creditBalance.totalLimit);
      const beforeUsed = Number(creditBalance.usedAmount);
      const newTotalLimit = beforeTotal + adjustAmount;

      if (newTotalLimit < 0) {
        throw new Error('调整后的总额度不能为负数');
      }

      if (newTotalLimit < beforeUsed) {
        throw new Error('调整后的总额度不能小于已用额度');
      }

      // 更新额度
      creditBalance.totalLimit = newTotalLimit;
      creditBalance.updatedAt = new Date();
      await repository.save(creditBalance);

      // 记录变更日志
      await this.createBalanceLog({
        manager,
        logRepository,
        tenantId,
        userId,
        changeType: CreditBalanceChangeType.ADJUST,
        changeAmount: adjustAmount,
        beforeTotal,
        afterTotal: newTotalLimit,
        beforeUsed,
        afterUsed: beforeUsed,
        referenceId: null,
        operatorId,
        remark
      });

      return creditBalance;
    });
  }

  /**
   * 扣减信用额度（用于支付）
   */
  async deductAmount(params: DeductAmountParams): Promise<CreditBalanceMt> {
    const { tenantId, userId, amount, referenceId, operatorId, remark } = params;

    if (amount <= 0) {
      throw new Error('扣减金额必须大于0');
    }

    return await this.dataSource.transaction(async (manager) => {
      const repository = manager.getRepository(CreditBalanceMt);
      const logRepository = manager.getRepository(CreditBalanceLogMt);

      // 查找信用额度记录
      const creditBalance = await repository.findOne({
        where: { tenantId, userId }
      });

      if (!creditBalance) {
        throw new Error('用户信用额度记录不存在');
      }

      if (creditBalance.isEnabled === 0) {
        throw new Error('用户信用额度已禁用');
      }

      const beforeTotal = Number(creditBalance.totalLimit);
      const beforeUsed = Number(creditBalance.usedAmount);
      const newUsedAmount = beforeUsed + amount;

      // 检查额度是否足够
      if (newUsedAmount > beforeTotal) {
        throw new Error('信用额度不足');
      }

      // 更新已用额度
      creditBalance.usedAmount = newUsedAmount;
      creditBalance.updatedAt = new Date();
      await repository.save(creditBalance);

      // 记录变更日志
      await this.createBalanceLog({
        manager,
        logRepository,
        tenantId,
        userId,
        changeType: CreditBalanceChangeType.PAYMENT,
        changeAmount: -amount, // 扣减为负数
        beforeTotal,
        afterTotal: beforeTotal,
        beforeUsed,
        afterUsed: newUsedAmount,
        referenceId: referenceId || null,
        operatorId,
        remark
      });

      return creditBalance;
    });
  }

  /**
   * 恢复信用额度（用于取消订单、退款等）
   */
  async restoreAmount(params: RestoreAmountParams): Promise<CreditBalanceMt> {
    const { tenantId, userId, amount, changeType, referenceId, operatorId, remark } = params;

    if (amount <= 0) {
      throw new Error('恢复金额必须大于0');
    }

    return await this.dataSource.transaction(async (manager) => {
      const repository = manager.getRepository(CreditBalanceMt);
      const logRepository = manager.getRepository(CreditBalanceLogMt);

      // 查找信用额度记录
      const creditBalance = await repository.findOne({
        where: { tenantId, userId }
      });

      if (!creditBalance) {
        throw new Error('用户信用额度记录不存在');
      }

      const beforeTotal = Number(creditBalance.totalLimit);
      const beforeUsed = Number(creditBalance.usedAmount);
      const newUsedAmount = beforeUsed - amount;

      // 检查恢复金额是否合理
      if (newUsedAmount < 0) {
        throw new Error('恢复金额不能超过已用额度');
      }

      // 更新已用额度
      creditBalance.usedAmount = newUsedAmount;
      creditBalance.updatedAt = new Date();
      await repository.save(creditBalance);

      // 记录变更日志
      await this.createBalanceLog({
        manager,
        logRepository,
        tenantId,
        userId,
        changeType,
        changeAmount: amount, // 恢复为正数
        beforeTotal,
        afterTotal: beforeTotal,
        beforeUsed,
        afterUsed: newUsedAmount,
        referenceId: referenceId || null,
        operatorId,
        remark
      });

      return creditBalance;
    });
  }

  /**
   * 取消订单恢复额度（供订单模块调用）
   */
  async restoreBalanceForCancelOrder(
    tenantId: number,
    userId: number,
    orderId: string,
    amount: number,
    operatorId?: number
  ): Promise<CreditBalanceMt> {
    return this.restoreAmount({
      tenantId,
      userId,
      amount,
      changeType: CreditBalanceChangeType.CANCEL_ORDER,
      referenceId: orderId,
      operatorId,
      remark: `取消订单恢复额度，订单号：${orderId}`
    });
  }

  /**
   * 退款恢复额度（供支付模块调用）
   */
  async restoreBalanceForRefund(
    tenantId: number,
    userId: number,
    orderId: string,
    refundAmount: number,
    operatorId?: number
  ): Promise<CreditBalanceMt> {
    return this.restoreAmount({
      tenantId,
      userId,
      amount: refundAmount,
      changeType: CreditBalanceChangeType.REFUND,
      referenceId: orderId,
      operatorId,
      remark: `退款恢复额度，订单号：${orderId}，退款金额：${refundAmount}`
    });
  }

  /**
   * 查询用户信用额度
   */
  async getBalance(tenantId: number, userId: number): Promise<CreditBalanceMt | null> {
    return await this.creditBalanceRepository.findOne({
      where: { tenantId, userId }
    });
  }

  /**
   * 查询用户额度变更记录
   */
  async getBalanceLogs(
    tenantId: number,
    userId: number,
    page: number = 1,
    pageSize: number = 20
  ): Promise<[CreditBalanceLogMt[], number]> {
    const skip = (page - 1) * pageSize;

    return await this.creditBalanceLogRepository.findAndCount({
      where: { tenantId, userId },
      order: { createdAt: 'DESC' },
      skip,
      take: pageSize
    });
  }

  /**
   * 创建额度变更日志（内部方法）
   */
  private async createBalanceLog(params: {
    manager: EntityManager;
    logRepository: Repository<CreditBalanceLogMt>;
    tenantId: number;
    userId: number;
    changeType: CreditBalanceChangeType;
    changeAmount: number;
    beforeTotal: number | null;
    afterTotal: number | null;
    beforeUsed: number | null;
    afterUsed: number | null;
    referenceId: string | null;
    operatorId?: number;
    remark?: string;
  }): Promise<CreditBalanceLogMt> {
    const {
      logRepository,
      tenantId,
      userId,
      changeType,
      changeAmount,
      beforeTotal,
      afterTotal,
      beforeUsed,
      afterUsed,
      referenceId,
      operatorId,
      remark
    } = params;

    const log = logRepository.create({
      tenantId,
      userId,
      changeType,
      changeAmount,
      beforeTotal,
      afterTotal,
      beforeUsed,
      afterUsed,
      referenceId,
      operatorId,
      remark
    });

    return await logRepository.save(log);
  }
}