import { Controller, Get, Res } from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { Response } from 'express';
import { AlertHealthService } from './modules/alert/alert-health.service';

@ApiTags('aws')
@Controller()
export class AppController {
  constructor(private readonly health: AlertHealthService) {}

  /**
   * 프로세스 생존만 본다(liveness). 의존성 상태와 무관하게 **항상 200** 이다.
   *
   * `GET /health` 가 실측 + 실패 시 503 으로 바뀌었으므로(M2 §D6 단계 3), 재시작으로
   * 해결되지 않는 장애(DB 다운)에 태스크를 순환시키지 않으려면 프로브는 이쪽을 봐야 한다.
   * 인프라 프로브를 붙일 때 liveness = `/live`, readiness = `/health` 로 나눈다.
   */
  @ApiOperation({ summary: 'Liveness check (상수)' })
  @Get('live')
  liveness() {
    return 'OK';
  }

  /**
   * 의존성 실측 헬스(readiness) — UI3. DB·Redis 프로브가 하나라도 실패하면 **503** 이다.
   *
   * Q7 확인 결과(2026-08-11 사용자) 이 경로를 프로브하는 ALB 타깃그룹이 **없고**,
   * `deploy/deploy-api.sh:67` 은 확인용 curl 명령을 출력만 할 뿐 실행하지 않는다.
   * 즉 상태코드로 판정하는 자동 소비자가 현재 0 건이므로 순환 재시작 위험 없이 교체한다.
   *
   * **예외를 던지지 않고 상태코드만 세팅한다.** `AllExceptionFilter` 를 태우면 그 필터가
   * `bump5xx()` 를 호출해(§M2 D3) 이 응답이 A4(5xx 급증)의 분자로 잡힌다. 헬스 프로브는
   * 주기적으로 호출되므로 DB 장애 한 번이 A4 오탐까지 만들어 낸다 — 같은 장애를 두 번
   * 세는 셈이고, 분자만 늘어 실패율이 실제보다 부풀려진다.
   *
   * 본문은 `/health/detail` 과 같은 형태이고 예외 메시지를 싣지 않는다(무인증 경로).
   */
  @ApiOperation({
    summary: 'Health check (실측)',
    description: 'DB·Redis 프로브. 하나라도 실패하면 503. 본문은 /health/detail 과 동일.',
  })
  @Get('health')
  async healthCheck(@Res({ passthrough: true }) res: Response) {
    const body = await this.probe();
    if (!body.ok) res.status(503);
    return body;
  }

  /**
   * 의존성 실측 헬스(M2 §D6 단계 1). **항상 HTTP 200 이고 판정은 본문에 있다** —
   * 상태코드로 판정하는 소비자(배포 스크립트·ALB)를 건드리지 않기 위해서다.
   * A6 규칙의 API 축이 이 본문의 `ok` 를 읽는다.
   *
   * `redis` 가 `null` 이면 미구성(장애 아님)이며 `ok` 판정에서 제외된다.
   *
   * **본문에 예외 메시지를 싣지 않는다.** 이 경로는 인증이 없고 A6 규칙은 `ok` 만 읽는데,
   * 프로브의 `error` 는 드라이버 원문(호스트·포트·계정·스키마·SQL 상태)을 그대로 담는다.
   * 진단이 필요하면 서버 로그에 남은 원문을 본다 — 무인증 경로로 내보낼 이유가 없다.
   */
  @ApiOperation({
    summary: 'Health check (실측)',
    description: 'DB·Redis 프로브 결과. 항상 200 이며 판정은 본문 ok 필드에 있다.',
  })
  @Get('health/detail')
  async healthDetail() {
    return this.probe();
  }

  private async probe() {
    const [db, redis] = await Promise.all([
      this.health.probeDb(),
      this.health.probeRedis(),
    ]);
    return {
      ok: db.ok && (redis === null || redis.ok),
      db: { ok: db.ok, ms: db.ms },
      redis: redis === null ? null : { ok: redis.ok, ms: redis.ms },
    };
  }
}
