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

@Injectable()
export class OutboxRepository {
  /**
   * 退避时间配置（毫秒）
   * 第1次: 30s, 第2次: 1min, 第3次: 2min, 第4次: 5min,
   * 第5次: 10min, 第6次: 30min, 第7次: 1h, 第8次: 2h, 第9次: 4h, 第10次: 4h
   */
  private readonly BACKOFF_INTERVALS = [
    30_000, // 30 seconds
    60_000, // 1 minute
    120_000, // 2 minutes
    300_000, // 5 minutes
    600_000, // 10 minutes
    1_800_000, // 30 minutes
    3_600_000, // 1 hour
    7_200_000, // 2 hours
    14_400_000, // 4 hours (max)
    14_400_000, // 4 hours (max)
  ];

  constructor(private readonly prisma: PrismaService) {}

  /**
   * 创建 Outbox 事件
   */
  async create(data: {
    aggregateType: string;
    aggregateId: string;
    eventType: string;
    payload: any;
    topic?: string;
    key?: string;
  }): Promise<OutboxEvent> {
    return this.prisma.outboxEvent.create({
      data: {
        aggregateType: data.aggregateType,
        aggregateId: data.aggregateId,
        eventType: data.eventType,
        payload: data.payload,
        topic: data.topic || `mining-wallet.${data.eventType}`,
        key: data.key || data.aggregateId,
        status: OutboxStatus.PENDING,
      },
    });
  }

  /**
   * 获取待处理的事件（PENDING 状态且到达重试时间）
   */
  async findPendingEvents(limit: number = 100): Promise<OutboxEvent[]> {
    return this.prisma.outboxEvent.findMany({
      where: {
        status: OutboxStatus.PENDING,
        OR: [{ nextRetryAt: null }, { nextRetryAt: { lte: new Date() } }],
      },
      orderBy: { createdAt: 'asc' },
      take: limit,
    });
  }

  /**
   * 标记事件为已发布
   */
  async markAsPublished(id: string): Promise<void> {
    await this.prisma.outboxEvent.update({
      where: { id },
      data: {
        status: OutboxStatus.PUBLISHED,
        publishedAt: new Date(),
      },
    });
  }

  /**
   * 标记事件发布失败，计算下次重试时间（指数退避，最大4小时）
   */
  async markAsFailed(
    id: string,
    error: string,
    currentRetryCount: number,
    maxRetries: number,
  ): Promise<void> {
    const newRetryCount = currentRetryCount + 1;
    const shouldFail = newRetryCount >= maxRetries;

    // 使用预定义的退避时间表
    const backoffIndex = Math.min(
      newRetryCount - 1,
      this.BACKOFF_INTERVALS.length - 1,
    );
    const delayMs = this.BACKOFF_INTERVALS[backoffIndex];

    await this.prisma.outboxEvent.update({
      where: { id },
      data: {
        retryCount: newRetryCount,
        lastError: error,
        status: shouldFail ? OutboxStatus.FAILED : OutboxStatus.PENDING,
        nextRetryAt: shouldFail ? null : new Date(Date.now() + delayMs),
      },
    });
  }

  /**
   * 删除已发布的旧事件
   */
  async deletePublished(before: Date): Promise<number> {
    const result = await this.prisma.outboxEvent.deleteMany({
      where: {
        status: OutboxStatus.PUBLISHED,
        publishedAt: { lt: before },
      },
    });
    return result.count;
  }

  /**
   * 重置长时间失败的事件（可选：给予重试机会）
   */
  async resetFailedEvents(olderThan: Date): Promise<number> {
    const result = await this.prisma.outboxEvent.updateMany({
      where: {
        status: OutboxStatus.FAILED,
        createdAt: { gt: olderThan },
      },
      data: {
        status: OutboxStatus.PENDING,
        retryCount: 0,
        nextRetryAt: new Date(),
      },
    });
    return result.count;
  }
}
