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

@Injectable()
export class OutboxRepository {
  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.${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(),
      },
    });
  }

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

    // 指数退避: 30s, 60s, 120s, 240s, 480s, 960s, 1920s, 3840s, 7680s, 10800s (最大3小时)
    const baseDelayMs = 30000; // 30 seconds
    const maxDelayMs = 3 * 60 * 60 * 1000; // 3 hours
    const delayMs = Math.min(
      baseDelayMs * Math.pow(2, newRetryCount - 1),
      maxDelayMs,
    );

    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;
  }
}
