import { randomBytes } from 'crypto';
import type { Request } from 'express';

// OAuth CSRF 방지용 state 저장소 — 세션을 쓰지 않는 무상태(JWT) 구조라
// passport-oauth2 의 기본 SessionStore 대신 httpOnly 쿠키에 1회용 난수 state 를 보관한다.
// 인가 시작 시 store() 로 쿠키 발급 + state 반환(인가 URL 에 부착), 콜백에서 verify() 로
// 쿠키와 제공자 반환 state 일치를 확인한다(불일치 시 인증 거부).
const STATE_COOKIE = 'oauth_state';
const STATE_TTL_MS = 10 * 60 * 1000; // 10분

type StoreCallback = (err: Error | null, state?: string) => void;
type VerifyCallback = (
  err: Error | null,
  ok?: boolean,
  info?: { message: string },
) => void;

function cookieOptions() {
  const isProd = process.env.NODE_ENV === 'production';
  return {
    httpOnly: true,
    secure: isProd,
    sameSite: 'lax' as const,
    path: '/api/auth',
    maxAge: STATE_TTL_MS,
  };
}

export class CookieStateStore {
  // passport 버전에 따라 (req, cb) 또는 (req, meta, cb) 로 호출되므로 마지막 인자를 콜백으로 사용
  store(req: Request, ...rest: unknown[]): void {
    const cb = rest[rest.length - 1] as StoreCallback;
    const state = randomBytes(16).toString('hex');
    req.res?.cookie(STATE_COOKIE, state, cookieOptions());
    cb(null, state);
  }

  verify(req: Request, providedState: string, ...rest: unknown[]): void {
    const cb = rest[rest.length - 1] as VerifyCallback;
    const cookies = req.cookies as Record<string, string> | undefined;
    const expected = cookies?.[STATE_COOKIE];
    // 1회용 — 검증 직후 쿠키 제거
    req.res?.clearCookie(STATE_COOKIE, { path: '/api/auth' });
    if (!expected || expected !== providedState) {
      cb(null, false, { message: 'OAuth state 불일치' });
      return;
    }
    cb(null, true);
  }
}
