import {
  CanActivate,
  ExecutionContext,
  HttpException,
  HttpStatus,
  Injectable,
  Logger,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Request } from 'express';
import Redis from 'ioredis';

/**
 * ★보안(헤드라인 #1 완화): /treat/certify 는 비로그인 capability(altCode/agcCode) 기반 공개 엔드포인트라
 *   세션 가드를 못 붙인다(B2C·알림톡 비로그인). 대신 폰/IP 단위 레이트리밋으로 "남 명의 신원 살포" 남용을
 *   막는다. (조사 결론: certify 남용은 인증가드가 아니라 레이트리밋 레이어가 맞다.)
 *
 *   ★게이트(무중단/실유저 보호): RATE_LIMIT_CERTIFY_PER_MIN=0(기본)이면 무제한 = 동작 무변. >0 일 때만 활성.
 *   실유저(가족 공유IP·정상 재시도)를 자르지 않게 임계값은 운영자가 신중히 정해 켠다.
 *   ★Redis 장애 시 fail-OPEN(통과) — 인프라 장애로 실유저를 막지 않는다('실유저 안 자르기' 원칙).
 */
@Injectable()
export class CertifyRateLimitGuard implements CanActivate {
  private readonly logger = new Logger(CertifyRateLimitGuard.name);
  private readonly redis: Redis | null;
  private readonly perMin: number;

  constructor(private readonly config: ConfigService) {
    const url = this.config.get<string>('redisUrl');
    this.redis = url ? new Redis(url, { maxRetriesPerRequest: 1 }) : null;
    this.perMin = Number(process.env.RATE_LIMIT_CERTIFY_PER_MIN ?? 0);
  }

  async canActivate(context: ExecutionContext): Promise<boolean> {
    if (this.perMin <= 0 || !this.redis) return true; // 게이트 OFF = 동작 무변
    const req = context.switchToHttp().getRequest<Request>();
    const body = (req.body ?? {}) as { usrPhone?: string };
    const phone = (body.usrPhone ?? '').replace(/[^0-9]/g, '');
    const ip =
      (req.headers['x-forwarded-for'] as string | undefined)
        ?.split(',')[0]
        ?.trim() ||
      req.ip ||
      '';
    const id = phone || ip;
    if (!id) return true; // 식별 불가면 통과(차단 안 함)
    try {
      const key = `certify:rl:${id}`;
      const n = await this.redis.incr(key);
      if (n === 1) await this.redis.pexpire(key, 60000); // 1분 윈도우
      if (n > this.perMin) {
        this.logger.warn(
          `certify 레이트리밋 초과 (${phone ? 'phone' : 'ip'} 기준) n=${n}/${this.perMin}/분`,
        );
        throw new HttpException(
          '요청이 많습니다. 잠시 후 다시 시도해주세요.',
          HttpStatus.TOO_MANY_REQUESTS,
        );
      }
    } catch (e) {
      if (e instanceof HttpException) throw e; // 레이트리밋 차단은 전파
      return true; // ★Redis 장애 = fail-open(실유저 안 자름)
    }
    return true;
  }
}
