import {
  Injectable,
  Logger,
  OnApplicationShutdown,
  OnModuleInit,
} from "@nestjs/common";
import { closeDb } from "../../config/database.js";
import { closeEs } from "../../config/elasticsearch.js";
import { connectKafka, disconnectKafka } from "../kafka/kafka.client.js";
import { closeRedis } from "../redis/redis.client.js";
import { outboxPublisher } from "../outbox/outbox.publisher.js";

const SERVICE_NAME = "msg";

/**
 * 优雅停机服务 + 资源生命周期管理。
 *
 * 仲裁依据 G2：统一管理 DB/ES/Redis/Kafka/OutboxPublisher 生命周期。
 *
 * 启动顺序（onModuleInit）：
 * 1. connectKafka()：连接 producer + consumer（幂等，KafkaConsumerService 也会调用）
 * 2. outboxPublisher.start()：启动轮询 worker，投递 pending 事件到 Kafka
 *
 * 关闭顺序（onApplicationShutdown，在 OnModuleDestroy 之后执行）：
 * 1. outboxPublisher.stop()：停止轮询
 * 2. disconnectKafka()：断开 producer + consumer
 * 3. closeRedis()：关闭 Redis 连接
 * 4. closeEs()：关闭 ES 客户端
 * 5. closeDb()：关闭 MySQL 连接池
 *
 * 注：KafkaConsumerService 实现 OnModuleDestroy，其 stop() 会在
 * onApplicationShutdown 之前被 NestJS 调用。disconnectKafka() 是幂等的。
 */
@Injectable()
export class LifecycleService implements OnModuleInit, OnApplicationShutdown {
  private readonly logger = new Logger(LifecycleService.name);

  async onModuleInit(): Promise<void> {
    // 1. 连接 Kafka（producer + consumer）
    await connectKafka();

    // 2. 启动 Outbox publisher 轮询
    await outboxPublisher.start();

    this.logger.log(`service ${SERVICE_NAME} module initialized`);
  }

  async onApplicationShutdown(signal?: string): Promise<void> {
    this.logger.log(
      `service ${SERVICE_NAME} shutting down (signal=${signal ?? "unknown"})`,
    );

    // 按依赖反序关闭
    await this.safeStop("outboxPublisher", () => outboxPublisher.stop());
    await this.safeStop("kafka", () => disconnectKafka());
    await this.safeStop("redis", () => closeRedis());
    await this.safeStop("elasticsearch", () => closeEs());
    await this.safeStop("drizzle", () => closeDb());

    this.logger.log(`service ${SERVICE_NAME} shutdown complete`);
  }

  private async safeStop(
    name: string,
    fn: () => Promise<unknown>,
  ): Promise<void> {
    try {
      await fn();
      this.logger.log(`${name} closed`);
    } catch (error) {
      this.logger.error(
        `${name} close failed: ${error instanceof Error ? error.message : String(error)}`,
      );
    }
  }
}
