import type { MySql2Database } from "drizzle-orm/mysql2";
import type { Producer } from "kafkajs";
import type { Logger } from "pino";

/**
 * Outbox 记录状态机：
 * - pending：已写入 outbox 表，等待 publisher 投递
 * - published：已成功投递到 Kafka
 * - failed：重试次数耗尽，需人工介入
 */
export type OutboxStatus = "pending" | "published" | "failed";

/**
 * Outbox 模块配置（由各服务在 forRoot 时提供）。
 */
export interface OutboxConfig {
  /** outbox 表名（每服务独立，如 core_edu_outbox） */
  tableName: string;
  /** Kafka 投递目标 topic */
  kafkaTopic: string;
  /** 轮询 pending 记录的间隔（毫秒） */
  pollIntervalMs: number;
  /** 每次轮询批量处理的记录数 */
  batchSize: number;
  /** 单条记录最大重试次数 */
  maxRetryCount: number;
  /** 重试退避基数（毫秒），实际退避 = retryBackoffMs * 2^retryCount */
  retryBackoffMs: number;
}

/**
 * publish 选项。
 *
 * `tx` 用于由调用方管理事务：调用方在 `db.transaction(async (tx) => { ... })`
 * 中同时写入业务表与 outbox 记录，保证两者原子提交（事务性 Outbox 模式）。
 */
export interface PublishOptions {
  /** 聚合根 ID，作为 Kafka 消息 key 用于分区与顺序保证 */
  aggregateId?: string;
  /** 附加元数据，写入 outbox 行并随消息头投递 */
  metadata?: Record<string, string>;
  /** 延迟投递毫秒数；设置后 nextRetryAt 推迟，到期前不会被轮询 */
  delayMs?: number;
  /** 调用方管理的事务客户端；省略时使用模块注入的 db（非事务单条插入） */
  tx?: OutboxDbClient;
}

/**
 * Outbox 行结构（与 schema.ts 的 $inferSelect 对齐）。
 */
export interface OutboxRecord {
  id: string;
  eventType: string;
  /** JSON 序列化后的 payload */
  payload: string;
  aggregateId: string;
  status: OutboxStatus;
  retryCount: number;
  maxRetryCount: number;
  nextRetryAt: Date | null;
  createdAt: Date;
  publishedAt: Date | null;
  lastError: string | null;
  metadata: Record<string, string> | null;
}

/**
 * Drizzle 数据库客户端类型。
 *
 * 服务以无 schema 绑定方式创建 db（`drizzle(pool)`），其类型为
 * `MySql2Database<Record<string, never>>`。由于 `MySql2Transaction extends
 * MySqlDatabase`，事务客户端同样可赋值给本类型，故 publish 可接受调用方传入的 tx。
 */
export type OutboxDbClient = MySql2Database<Record<string, never>>;

/** Kafka producer 类型别名 */
export type OutboxKafkaProducer = Producer;

/** pino Logger 类型别名 */
export type OutboxLogger = Logger;

// —— NestJS 注入 Token —— //
export const OUTBOX_CONFIG = Symbol("OUTBOX_CONFIG");
export const OUTBOX_DB = Symbol("OUTBOX_DB");
export const OUTBOX_TABLE = Symbol("OUTBOX_TABLE");
export const OUTBOX_KAFKA_PRODUCER = Symbol("OUTBOX_KAFKA_PRODUCER");
export const OUTBOX_LOGGER = Symbol("OUTBOX_LOGGER");
