import { Module, type DynamicModule } from "@nestjs/common";
import pino from "pino";
import { OutboxService } from "./outbox.service.js";
import { OutboxPublisher } from "./publisher.js";
import { createOutboxTable } from "./schema.js";
import {
  OUTBOX_CONFIG,
  OUTBOX_DB,
  OUTBOX_KAFKA_PRODUCER,
  OUTBOX_LOGGER,
  OUTBOX_TABLE,
  type OutboxConfig,
  type OutboxDbClient,
  type OutboxKafkaProducer,
  type OutboxLogger,
} from "./types.js";

/**
 * forRoot 选项。
 *
 * `config` 即 OutboxConfig（表名 / topic / 轮询参数）。
 * `db` / `kafkaProducer` / `logger` 为基础设施实例：NestJS DynamicModule
 * 无法跨模块边界注入宿主已存在的具体单例，故统一通过 forRoot 注入。
 *
 * `kafkaProducer` 应由调用方以 idempotent 方式创建：
 * `kafka.producer({ idempotent: true, transactionalId: '<service>-tx' })`。
 */
export interface OutboxRootOptions {
  config: OutboxConfig;
  db: OutboxDbClient;
  kafkaProducer: OutboxKafkaProducer;
  /** 可选；省略时使用默认 pino logger（name=outbox） */
  logger?: OutboxLogger;
}

function resolveLogger(logger: OutboxLogger | undefined): OutboxLogger {
  return logger ?? pino({ name: "outbox", level: "info" });
}

/**
 * OutboxModule —— 事务性 Outbox 模式的 NestJS DynamicModule。
 *
 * 注册并启动：
 * - OutboxService：业务代码注入后调用 `publish` 写入 outbox 记录
 * - OutboxPublisher：onModuleInit 启动轮询，onModuleDestroy 停止轮询
 *
 * 用法：
 * ```ts
 * @Module({
 *   imports: [OutboxModule.forRoot({ config, db, kafkaProducer })],
 * })
 * export class AppModule {}
 * ```
 */
@Module({})
export class OutboxModule {
  static forRoot(options: OutboxRootOptions): DynamicModule {
    const table = createOutboxTable(options.config.tableName);
    const logger = resolveLogger(options.logger);

    return {
      module: OutboxModule,
      providers: [
        { provide: OUTBOX_CONFIG, useValue: options.config },
        { provide: OUTBOX_DB, useValue: options.db },
        { provide: OUTBOX_TABLE, useValue: table },
        { provide: OUTBOX_KAFKA_PRODUCER, useValue: options.kafkaProducer },
        { provide: OUTBOX_LOGGER, useValue: logger },
        OutboxService,
        OutboxPublisher,
      ],
      exports: [OutboxService],
    };
  }
}
