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

export enum OutboxStatus {
  PENDING = 'PENDING',      // 待发送
  SENT = 'SENT',            // 已发送到 Kafka，等待消费方确认
  CONFIRMED = 'CONFIRMED',  // 消费方已确认处理成功
  FAILED = 'FAILED',        // 发送失败，等待重试
}

export interface OutboxEventData {
  eventType: string;
  topic: string;
  key: string;
  payload: Record<string, unknown>;
  aggregateId: string;
  aggregateType: string;
}

export interface OutboxEvent extends OutboxEventData {
  id: bigint;
  status: OutboxStatus;
  retryCount: number;
  maxRetries: number;
  lastError: string | null;
  createdAt: Date;
  publishedAt: Date | null;
  nextRetryAt: Date | null;
}

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

  constructor(private readonly prisma: PrismaService) {}

  /**
   * 在事务中保存 Outbox 事件
   */
  async saveInTransaction(
    tx: Prisma.TransactionClient,
    events: OutboxEventData[],
  ): Promise<void> {
    if (events.length === 0) return;

    this.logger.debug(`[OUTBOX] Saving ${events.length} events to outbox (in transaction)`);

    await tx.outboxEvent.createMany({
      data: events.map((event) => ({
        eventType: event.eventType,
        topic: event.topic,
        key: event.key,
        payload: event.payload as Prisma.JsonObject,
        aggregateId: event.aggregateId,
        aggregateType: event.aggregateType,
        status: OutboxStatus.PENDING,
      })),
    });

    this.logger.debug(`[OUTBOX] Saved ${events.length} events to outbox`);
  }

  /**
   * 直接保存 Outbox 事件（不在事务中）
   */
  async saveEvents(events: OutboxEventData[]): Promise<void> {
    if (events.length === 0) return;

    this.logger.debug(`[OUTBOX] Saving ${events.length} events to outbox`);

    await this.prisma.outboxEvent.createMany({
      data: events.map((event) => ({
        eventType: event.eventType,
        topic: event.topic,
        key: event.key,
        payload: event.payload as Prisma.JsonObject,
        aggregateId: event.aggregateId,
        aggregateType: event.aggregateType,
        status: OutboxStatus.PENDING,
      })),
    });

    this.logger.log(`[OUTBOX] ✓ Saved ${events.length} events to outbox`);
  }

  /**
   * 获取待发布的事件（按创建时间排序）
   */
  async findPendingEvents(limit: number = 100): Promise<OutboxEvent[]> {
    const events = await this.prisma.outboxEvent.findMany({
      where: {
        status: OutboxStatus.PENDING,
      },
      orderBy: {
        createdAt: 'asc',
      },
      take: limit,
    });

    return events.map((e) => this.mapToOutboxEvent(e));
  }

  /**
   * 获取需要重试的事件
   */
  async findEventsForRetry(limit: number = 50): Promise<OutboxEvent[]> {
    const now = new Date();

    const events = await this.prisma.outboxEvent.findMany({
      where: {
        status: OutboxStatus.FAILED,
        retryCount: {
          lt: this.prisma.outboxEvent.fields.maxRetries,
        },
        nextRetryAt: {
          lte: now,
        },
      },
      orderBy: {
        nextRetryAt: 'asc',
      },
      take: limit,
    });

    return events.map((e) => this.mapToOutboxEvent(e));
  }

  /**
   * 标记事件为已发送（等待消费方确认）
   * B方案：发送到 Kafka 后先标记为 SENT，等消费方确认后才标记为 CONFIRMED
   */
  async markAsSent(id: bigint): Promise<void> {
    await this.prisma.outboxEvent.update({
      where: { id },
      data: {
        status: OutboxStatus.SENT,
        publishedAt: new Date(),
      },
    });

    this.logger.debug(`[OUTBOX] Marked event ${id} as SENT (awaiting consumer confirmation)`);
  }

  /**
   * 标记事件为已确认（消费方已成功处理）
   * B方案：收到消费方确认后调用
   * 使用 aggregateId + eventType 组合精确匹配，避免误确认同一订单的其他事件
   */
  async markAsConfirmed(eventId: string, eventType?: string): Promise<boolean> {
    const whereClause: Prisma.OutboxEventWhereInput = {
      aggregateId: eventId,
      status: OutboxStatus.SENT,
    };

    // 如果提供了 eventType，则精确匹配
    if (eventType) {
      whereClause.eventType = eventType;
    }

    const result = await this.prisma.outboxEvent.updateMany({
      where: whereClause,
      data: {
        status: OutboxStatus.CONFIRMED,
      },
    });

    if (result.count > 0) {
      this.logger.log(`[OUTBOX] ✓ Event ${eventId} (${eventType || 'all types'}) confirmed by consumer`);
      return true;
    }

    this.logger.warn(`[OUTBOX] Event ${eventId} (${eventType || 'any'}) not found or not in SENT status`);
    return false;
  }

  /**
   * 通过 outbox ID 标记为已确认
   */
  async markAsConfirmedById(id: bigint): Promise<void> {
    await this.prisma.outboxEvent.update({
      where: { id },
      data: {
        status: OutboxStatus.CONFIRMED,
      },
    });

    this.logger.log(`[OUTBOX] ✓ Event ${id} confirmed by consumer`);
  }

  /**
   * 获取已发送但未确认且超时的事件（用于重试）
   * B方案：超时未收到确认的事件需要重发
   */
  async findSentEventsTimedOut(timeoutMinutes: number = 5, limit: number = 50): Promise<OutboxEvent[]> {
    const cutoffTime = new Date();
    cutoffTime.setMinutes(cutoffTime.getMinutes() - timeoutMinutes);

    const events = await this.prisma.outboxEvent.findMany({
      where: {
        status: OutboxStatus.SENT,
        publishedAt: {
          lt: cutoffTime,
        },
        retryCount: {
          lt: 5, // 最多重试5次
        },
      },
      orderBy: {
        publishedAt: 'asc',
      },
      take: limit,
    });

    return events.map((e) => this.mapToOutboxEvent(e));
  }

  /**
   * 将超时的 SENT 事件重置为 PENDING 以便重发
   */
  async resetSentToPending(id: bigint): Promise<void> {
    const event = await this.prisma.outboxEvent.findUnique({
      where: { id },
    });

    if (!event) return;

    await this.prisma.outboxEvent.update({
      where: { id },
      data: {
        status: OutboxStatus.PENDING,
        retryCount: event.retryCount + 1,
        lastError: 'Consumer confirmation timeout',
      },
    });

    this.logger.warn(`[OUTBOX] Event ${id} reset to PENDING for retry (confirmation timeout)`);
  }

  /**
   * 标记事件为已发布（旧方法，保留兼容性）
   * @deprecated 使用 markAsSent 和 markAsConfirmed 代替
   */
  async markAsPublished(id: bigint): Promise<void> {
    await this.prisma.outboxEvent.update({
      where: { id },
      data: {
        status: OutboxStatus.CONFIRMED,
        publishedAt: new Date(),
      },
    });

    this.logger.debug(`[OUTBOX] Marked event ${id} as published (confirmed)`);
  }

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

    if (!event) return;

    const newRetryCount = event.retryCount + 1;
    const isFinalFailure = newRetryCount >= event.maxRetries;

    // 指数退避：1min, 2min, 4min, 8min, 16min
    const delayMinutes = Math.pow(2, newRetryCount - 1);
    const nextRetryAt = new Date();
    nextRetryAt.setMinutes(nextRetryAt.getMinutes() + delayMinutes);

    await this.prisma.outboxEvent.update({
      where: { id },
      data: {
        status: isFinalFailure ? OutboxStatus.FAILED : OutboxStatus.FAILED,
        retryCount: newRetryCount,
        lastError: error,
        nextRetryAt: isFinalFailure ? null : nextRetryAt,
      },
    });

    if (isFinalFailure) {
      this.logger.error(`[OUTBOX] Event ${id} permanently failed after ${newRetryCount} retries: ${error}`);
    } else {
      this.logger.warn(`[OUTBOX] Event ${id} failed, retry ${newRetryCount}/${event.maxRetries}, next retry at ${nextRetryAt}`);
    }
  }

  /**
   * 批量标记为已确认
   * @deprecated 使用单个 markAsConfirmedById 代替
   */
  async markManyAsPublished(ids: bigint[]): Promise<void> {
    if (ids.length === 0) return;

    await this.prisma.outboxEvent.updateMany({
      where: {
        id: { in: ids },
      },
      data: {
        status: OutboxStatus.CONFIRMED,
        publishedAt: new Date(),
      },
    });

    this.logger.debug(`[OUTBOX] Marked ${ids.length} events as confirmed`);
  }

  /**
   * 清理已确认的旧事件（保留最近7天）
   */
  async cleanupOldEvents(retentionDays: number = 7): Promise<number> {
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - retentionDays);

    const result = await this.prisma.outboxEvent.deleteMany({
      where: {
        status: OutboxStatus.CONFIRMED,
        publishedAt: {
          lt: cutoffDate,
        },
      },
    });

    if (result.count > 0) {
      this.logger.log(`[OUTBOX] Cleaned up ${result.count} old events`);
    }

    return result.count;
  }

  /**
   * 获取 Outbox 统计信息
   */
  async getStats(): Promise<{
    pending: number;
    sent: number;
    confirmed: number;
    failed: number;
  }> {
    const [pending, sent, confirmed, failed] = await Promise.all([
      this.prisma.outboxEvent.count({ where: { status: OutboxStatus.PENDING } }),
      this.prisma.outboxEvent.count({ where: { status: OutboxStatus.SENT } }),
      this.prisma.outboxEvent.count({ where: { status: OutboxStatus.CONFIRMED } }),
      this.prisma.outboxEvent.count({ where: { status: OutboxStatus.FAILED } }),
    ]);

    return { pending, sent, confirmed, failed };
  }

  private mapToOutboxEvent(record: any): OutboxEvent {
    return {
      id: record.id,
      eventType: record.eventType,
      topic: record.topic,
      key: record.key,
      payload: record.payload as Record<string, unknown>,
      aggregateId: record.aggregateId,
      aggregateType: record.aggregateType,
      status: record.status as OutboxStatus,
      retryCount: record.retryCount,
      maxRetries: record.maxRetries,
      lastError: record.lastError,
      createdAt: record.createdAt,
      publishedAt: record.publishedAt,
      nextRetryAt: record.nextRetryAt,
    };
  }
}
