import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module.js";
import { env } from "./config/env.js";
import { connectKafka, disconnectKafka } from "./config/kafka.js";
import { connectRedis, disconnectRedis } from "./config/redis.js";
import { outboxPublisher } from "./shared/outbox/outbox.publisher.js";
import { GlobalErrorFilter } from "./shared/errors/global-error.filter.js";
import { initTracer, shutdownTracer } from "./shared/observability/tracer.js";
import { logger } from "./shared/observability/logger.js";
import { registry } from "./shared/observability/metrics.js";
import type { Request, Response } from "express";

async function bootstrap(): Promise<void> {
  initTracer();

  const app = await NestFactory.create(AppModule, { bufferLogs: true });
  app.useGlobalFilters(new GlobalErrorFilter());
  app.enableShutdownHooks();

  // Prometheus 指标端点：不鉴权，供 Prometheus 抓取。
  app.getHttpAdapter().get("/metrics", async (_req: Request, res: Response) => {
    res.set("Content-Type", registry.contentType);
    res.end(await registry.metrics());
  });

  // Connect Kafka producer/consumer before starting the outbox publisher.
  // Non-blocking: if Kafka is unavailable, service still starts; outbox
  // publisher will retry sends and messages stay pending until Kafka recovers.
  void connectKafka();

  // Connect Redis for distributed lock (homework submission idempotency).
  // Non-blocking: if Redis is unavailable, service still starts; lock-based
  // operations will fall back to DB unique index for idempotency.
  void connectRedis();

  // Start the transactional outbox publisher - polls pending messages
  // and publishes them to Kafka topics defined in TOPIC_MAP.
  await outboxPublisher.start();

  await app.listen(env.PORT);
  logger.info(
    { port: env.PORT, grpcPort: env.GRPC_PORT, service: "core-edu" },
    "CoreEdu service is listening (HTTP; gRPC port reserved for P3)",
  );

  const shutdown = async (signal: string): Promise<void> => {
    logger.info({ signal }, "Shutting down gracefully...");
    await outboxPublisher.stop();
    await disconnectRedis();
    await disconnectKafka();
    await shutdownTracer();
    await app.close();
    process.exit(0);
  };

  process.on("SIGTERM", () => void shutdown("SIGTERM"));
  process.on("SIGINT", () => void shutdown("SIGINT"));
}

void bootstrap();
