import { and, asc, eq, isNull, lte, or, sql, type SQL } from "drizzle-orm";
import { getDb } from "../../config/database.js";
import { outboxEvents, type OutboxEvent } from "./outbox.schema.js";

/**
 * Outbox 数据访问层。
 *
 * 参照 core-edu outbox.repository 模式：
 * - findPending：拉取到期 pending 记录（nextRetryAt IS NULL 或 <= now）
 * - markPublished：投递成功后标记
 * - markFailed：重试耗尽标记 failed
 * - incrementRetry：失败时重试计数 +1 + 退避
 */
export async function findPending(batchSize: number): Promise<OutboxEvent[]> {
  const db = getDb();
  const now = new Date();
  const where: SQL | undefined = and(
    eq(outboxEvents.status, "pending"),
    or(isNull(outboxEvents.nextRetryAt), lte(outboxEvents.nextRetryAt, now)),
  );
  return db
    .select()
    .from(outboxEvents)
    .where(where)
    .orderBy(asc(outboxEvents.createdAt))
    .limit(batchSize);
}

export async function markPublished(eventId: string): Promise<void> {
  const db = getDb();
  await db
    .update(outboxEvents)
    .set({ status: "published", publishedAt: new Date(), lastError: null })
    .where(eq(outboxEvents.eventId, eventId));
}

export async function markFailed(
  eventId: string,
  error: string,
): Promise<void> {
  const db = getDb();
  await db
    .update(outboxEvents)
    .set({ status: "failed", lastError: error })
    .where(eq(outboxEvents.eventId, eventId));
}

export async function incrementRetry(
  eventId: string,
  error: string,
  backoffMs: number,
): Promise<void> {
  const db = getDb();
  const nextRetryAt = new Date(Date.now() + backoffMs);
  await db
    .update(outboxEvents)
    .set({
      retryCount: sql`${outboxEvents.retryCount} + 1`,
      nextRetryAt,
      lastError: error,
    })
    .where(eq(outboxEvents.eventId, eventId));
}
