import { Injectable } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';

export enum CompensationType {
  UNFREEZE = 'UNFREEZE',           // 解冻资金
  REFUND = 'REFUND',               // 退款（已扣款的情况）
  RETRY_CONFIRM = 'RETRY_CONFIRM', // 重试确认扣款
  RETRY_ALLOCATE = 'RETRY_ALLOCATE', // 重试资金分配
}

export enum CompensationStatus {
  PENDING = 'PENDING',
  PROCESSING = 'PROCESSING',
  COMPLETED = 'COMPLETED',
  FAILED = 'FAILED',
}

export enum FailureStage {
  FREEZE = 'FREEZE',
  DB_TRANSACTION = 'DB_TRANSACTION',
  CONFIRM = 'CONFIRM',
  ALLOCATE = 'ALLOCATE',
}

export interface PaymentCompensationData {
  orderNo: string;
  userId: bigint;
  compensationType: CompensationType;
  amount: number;
  failureReason?: string;
  failureStage?: FailureStage;
}

export interface PaymentCompensationRecord {
  id: bigint;
  orderNo: string;
  userId: bigint;
  compensationType: CompensationType;
  amount: number;
  status: CompensationStatus;
  failureReason: string | null;
  failureStage: string | null;
  retryCount: number;
  maxRetries: number;
  nextRetryAt: Date | null;
  lastError: string | null;
  createdAt: Date;
  processedAt: Date | null;
  completedAt: Date | null;
}

type PrismaCompensationRecord = {
  id: bigint;
  orderNo: string;
  userId: bigint;
  compensationType: string;
  amount: Prisma.Decimal;
  status: string;
  failureReason: string | null;
  failureStage: string | null;
  retryCount: number;
  maxRetries: number;
  nextRetryAt: Date | null;
  lastError: string | null;
  createdAt: Date;
  processedAt: Date | null;
  completedAt: Date | null;
};

@Injectable()
export class PaymentCompensationRepository {
  constructor(private readonly prisma: PrismaService) {}

  /**
   * 创建补偿记录
   */
  async create(data: PaymentCompensationData): Promise<PaymentCompensationRecord> {
    const record = await this.prisma.paymentCompensation.create({
      data: {
        orderNo: data.orderNo,
        userId: data.userId,
        compensationType: data.compensationType,
        amount: new Prisma.Decimal(data.amount),
        status: CompensationStatus.PENDING,
        failureReason: data.failureReason,
        failureStage: data.failureStage,
        nextRetryAt: new Date(), // 立即可重试
      },
    });

    return this.mapToRecord(record);
  }

  /**
   * 查找待处理的补偿记录
   */
  async findPendingCompensations(limit: number = 100): Promise<PaymentCompensationRecord[]> {
    const records = await this.prisma.paymentCompensation.findMany({
      where: {
        status: {
          in: [CompensationStatus.PENDING, CompensationStatus.PROCESSING],
        },
        OR: [
          { nextRetryAt: null },
          { nextRetryAt: { lte: new Date() } },
        ],
      },
      orderBy: { createdAt: 'asc' },
      take: limit,
    });

    // 过滤掉已超过最大重试次数的记录
    return records
      .filter((r) => r.retryCount < r.maxRetries)
      .map((r) => this.mapToRecord(r));
  }

  /**
   * 标记为处理中
   */
  async markAsProcessing(id: bigint): Promise<void> {
    await this.prisma.paymentCompensation.update({
      where: { id },
      data: {
        status: CompensationStatus.PROCESSING,
        processedAt: new Date(),
      },
    });
  }

  /**
   * 标记为完成
   */
  async markAsCompleted(id: bigint): Promise<void> {
    await this.prisma.paymentCompensation.update({
      where: { id },
      data: {
        status: CompensationStatus.COMPLETED,
        completedAt: new Date(),
      },
    });
  }

  /**
   * 标记为失败并安排重试
   */
  async markAsFailedWithRetry(id: bigint, error: string): Promise<void> {
    const record = await this.prisma.paymentCompensation.findUnique({
      where: { id },
    });

    if (!record) return;

    const nextRetryCount = record.retryCount + 1;
    const isFinalFailure = nextRetryCount >= record.maxRetries;

    // 指数退避: 1min, 2min, 4min, 8min, 16min
    const delayMinutes = Math.pow(2, nextRetryCount - 1);
    const nextRetryAt = new Date(Date.now() + delayMinutes * 60 * 1000);

    await this.prisma.paymentCompensation.update({
      where: { id },
      data: {
        status: isFinalFailure ? CompensationStatus.FAILED : CompensationStatus.PENDING,
        retryCount: nextRetryCount,
        lastError: error,
        nextRetryAt: isFinalFailure ? null : nextRetryAt,
      },
    });
  }

  /**
   * 查找订单的补偿记录
   */
  async findByOrderNo(orderNo: string): Promise<PaymentCompensationRecord[]> {
    const records = await this.prisma.paymentCompensation.findMany({
      where: { orderNo },
      orderBy: { createdAt: 'desc' },
    });

    return records.map((r) => this.mapToRecord(r));
  }

  /**
   * 检查是否已存在补偿记录
   */
  async exists(orderNo: string, compensationType: CompensationType): Promise<boolean> {
    const count = await this.prisma.paymentCompensation.count({
      where: { orderNo, compensationType },
    });
    return count > 0;
  }

  /**
   * 获取统计信息
   */
  async getStats(): Promise<{
    pending: number;
    processing: number;
    completed: number;
    failed: number;
  }> {
    const [pending, processing, completed, failed] = await Promise.all([
      this.prisma.paymentCompensation.count({ where: { status: CompensationStatus.PENDING } }),
      this.prisma.paymentCompensation.count({ where: { status: CompensationStatus.PROCESSING } }),
      this.prisma.paymentCompensation.count({ where: { status: CompensationStatus.COMPLETED } }),
      this.prisma.paymentCompensation.count({ where: { status: CompensationStatus.FAILED } }),
    ]);

    return { pending, processing, completed, failed };
  }

  private mapToRecord(record: PrismaCompensationRecord): PaymentCompensationRecord {
    return {
      id: record.id,
      orderNo: record.orderNo,
      userId: record.userId,
      compensationType: record.compensationType as CompensationType,
      amount: record.amount.toNumber(),
      status: record.status as CompensationStatus,
      failureReason: record.failureReason,
      failureStage: record.failureStage,
      retryCount: record.retryCount,
      maxRetries: record.maxRetries,
      nextRetryAt: record.nextRetryAt,
      lastError: record.lastError,
      createdAt: record.createdAt,
      processedAt: record.processedAt,
      completedAt: record.completedAt,
    };
  }
}
