/**
 * Authenticated Fetch Gateway
 *
 * 인증이 필요한 백엔드 API 호출의 유일한 게이트웨이.
 * 401 감지 → refresh (globalThis 캐시) → retry 를 한 곳에서 처리.
 *
 * 이 모듈이 담당하는 것:
 *   - 요청 실행
 *   - 401 감지 → refreshAccessToken (globalThis 캐시로 프로세스 내 중복 방지)
 *   - 새 토큰으로 재시도
 *
 * 이 모듈이 담당하지 않는 것 (호출자가 처리):
 *   - 쿠키 읽기 (PROXY: request.cookies / Server Actions: cookies() API)
 *   - 쿠키 설정 (PROXY: Set-Cookie 헤더 / Server Actions: cookies() API)
 *   - 리다이렉트 (Server Actions: redirect('/login'))
 *   - 헤더 구성 (각 호출자가 자기 방식으로)
 */

import { refreshAccessToken, type RefreshResult } from './refresh-token';

export type AuthenticatedFetchResult = {
  /** 백엔드 응답 (성공이든 실패든) */
  response: Response;
  /** refresh 성공 시 새 토큰 (호출자가 쿠키에 저장) */
  newTokens?: RefreshResult;
  /** true면 인증 실패 (호출자가 쿠키 삭제 + 리다이렉트 처리) */
  authFailed?: boolean;
};

/**
 * 인증된 백엔드 요청 실행
 *
 * 반환값 상태:
 *   - { response }                    → 정상 (401 아님)
 *   - { response, newTokens }         → 401 → refresh 성공 → 재시도 성공
 *   - { response, authFailed: true }  → 인증 실패 (refresh 불가/실패/재시도 실패)
 *
 * @param url          백엔드 API URL
 * @param options      fetch 옵션 (호출자가 Authorization 등 헤더 포함)
 * @param refreshToken refresh_token (없으면 401 시 바로 실패 반환)
 * @param caller       호출자 이름 (로그용: 'PROXY' | 'serverFetch' | 'ServerApiClient')
 */
export async function authenticatedFetch(
  url: string,
  options: RequestInit,
  refreshToken: string | undefined,
  caller: string = 'unknown'
): Promise<AuthenticatedFetchResult> {
  // 1. 요청 실행 (호출자가 이미 모든 헤더 설정)
  const response = await fetch(url, options);

  // 2. 401이 아니면 그대로 반환
  if (response.status !== 401) {
    return { response };
  }

  // 3. 401이지만 refresh_token 없음 → 인증 실패
  if (!refreshToken) {
    console.warn(`🔴 [${caller}] 401 (no refresh token)`);
    return { response, authFailed: true };
  }

  // 4. 401 + refresh_token 있음 → 갱신 시도 (globalThis 캐시로 중복 방지)
  const refreshResult = await refreshAccessToken(refreshToken, caller);

  if (!refreshResult.success || !refreshResult.accessToken) {
    console.warn(`🔴 [${caller}] Token refresh failed`);
    return { response, authFailed: true };
  }

  // 5. 새 토큰으로 재시도
  const retryHeaders = new Headers(options.headers || {});
  retryHeaders.set('Authorization', `Bearer ${refreshResult.accessToken}`);

  const retryResponse = await fetch(url, {
    ...options,
    headers: retryHeaders,
  });


  // 6. 재시도도 401 → 인증 실패
  if (retryResponse.status === 401) {
    console.warn(`🔴 [${caller}] Retry still 401, auth failed`);
    return { response: retryResponse, authFailed: true };
  }

  // 7. 재시도 성공 → 새 토큰과 함께 반환 (호출자가 쿠키 설정)
  return {
    response: retryResponse,
    newTokens: refreshResult,
  };
}
