import { createId } from "@paralleldrive/cuid2";
import { getDb } from "../../config/database.js";
import { logger } from "../observability/logger.js";
import { resolveTopic } from "../kafka/topic-map.js";
import { outboxEvents } from "./outbox.schema.js";

/**
 * OutboxService —— 业务层写入 outbox 记录的接口。
 *
 * 调用方在业务事务内调用 publish()，将事件记录写入 msg_outbox_events 表，
 * 与业务写在同一事务中原子提交（事务性 Outbox 模式）。
 *
 * 由 OutboxPublisher 负责异步轮询 pending 记录并投递到 Kafka（at-least-once）。
 *
 * 仲裁依据 G11：eventId 用 cuid2，同时作为 Kafka 消息 key 实现幂等去重。
 */

export interface PublishOptions {
  /** 聚合根类型（如 "Notification"） */
  aggregateType: string;
  /** 聚合根 ID */
  aggregateId: string;
  /** 附加元数据，写入 outbox 行并随消息头投递 */
  metadata?: Record<string, string>;
  /** 延迟投递毫秒数 */
  delayMs?: number;
}

export interface PublishResult {
  eventId: string;
  topic: string;
}

/**
 * 写入一条 outbox 记录。
 *
 * @param eventType 事件类型（如 "notification.sent"），用于 TOPIC_MAP 路由
 * @param payload   事件负载（将被 JSON 序列化）
 * @param options   聚合信息 + 元数据 + 延迟
 * @returns eventId + 解析的 topic
 */
export async function publish(
  eventType: string,
  payload: unknown,
  options: PublishOptions,
): Promise<PublishResult> {
  const eventId = createId();
  const topic = resolveTopic(eventType);
  const now = new Date();
  const nextRetryAt =
    options.delayMs !== undefined && options.delayMs > 0
      ? new Date(now.getTime() + options.delayMs)
      : null;

  const db = getDb();
  await db.insert(outboxEvents).values({
    eventId,
    aggregateType: options.aggregateType,
    aggregateId: options.aggregateId,
    eventType,
    topic,
    payload: payload as Record<string, unknown>,
    status: "pending",
    retryCount: 0,
    maxRetryCount: 5,
    metadata: options.metadata ?? null,
    nextRetryAt,
  });

  logger.debug(
    { eventId, eventType, topic, aggregateId: options.aggregateId },
    "Outbox record enqueued",
  );
  return { eventId, topic };
}
