/**
 * 🔄 Refresh Token 공통 모듈
 *
 * 프록시(/api/proxy)와 serverFetch 양쪽에서 사용하는 공통 토큰 갱신 로직
 *
 * 문제: useEffect에서 여러 API 동시 호출 시 refresh_token 충돌
 * - 첫 번째 요청이 refresh_token 사용 → 성공 (토큰 폐기됨)
 * - 두 번째 요청이 같은 refresh_token 사용 → 실패 (이미 폐기됨)
 *
 * 해결: 5초간 refresh 결과 캐싱
 * - 동시 요청들이 같은 새 토큰을 공유
 * - 진행 중인 refresh Promise도 공유하여 중복 요청 방지
 * - 프록시와 serverFetch가 같은 캐시를 공유하여 더 효율적
 */

export type RefreshResult = {
  success: boolean;
  accessToken?: string;
  refreshToken?: string;
  expiresIn?: number;
};

// 캐시 상태 (globalThis 레벨에서 공유)
// ⚠️ 모듈 레벨 변수는 Next.js가 API route와 Server Actions를 별도 모듈로 컴파일하면
//    각각 다른 인스턴스를 가져서 캐시가 공유되지 않음
// → globalThis를 사용하여 같은 Node.js 프로세스 내 모든 모듈에서 동일한 캐시 참조
type RefreshCache = {
  promise: Promise<RefreshResult> | null;
  timestamp: number;
  result: RefreshResult | null;
};

const GLOBAL_CACHE_KEY = '__sam_refresh_token_cache__';

function getRefreshCache(): RefreshCache {
  if (!(globalThis as Record<string, unknown>)[GLOBAL_CACHE_KEY]) {
    (globalThis as Record<string, unknown>)[GLOBAL_CACHE_KEY] = {
      promise: null,
      timestamp: 0,
      result: null,
    };
  }
  return (globalThis as Record<string, unknown>)[GLOBAL_CACHE_KEY] as RefreshCache;
}

const REFRESH_CACHE_TTL = 5000; // 5초

/**
 * 실제 토큰 갱신 수행 (내부 함수)
 */
async function doRefreshToken(refreshToken: string): Promise<RefreshResult> {
  try {
    const refreshUrl = `${process.env.NEXT_PUBLIC_API_URL}/api/v1/refresh`;

    const response = await fetch(refreshUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        'X-API-KEY': process.env.API_KEY || '',
      },
      body: JSON.stringify({
        refresh_token: refreshToken,
      }),
    });

    if (!response.ok) {
      const errorBody = await response.text();
      console.warn('🔴 [RefreshToken] Token refresh failed:', {
        status: response.status,
        statusText: response.statusText,
        body: errorBody,
      });
      return { success: false };
    }

    // 방어적 JSON 파싱: PHP가 JSON 뒤에 warning을 붙여 보내는 경우 대응
    const { safeResponseJson } = await import('./safe-json-parse');
    const data = await safeResponseJson<{ access_token: string; refresh_token: string; expires_in: number }>(response);

    return {
      success: true,
      accessToken: data.access_token,
      refreshToken: data.refresh_token,
      expiresIn: data.expires_in,
    };
  } catch (error) {
    console.error('🔴 [RefreshToken] Token refresh error:', error);
    return { success: false };
  }
}

/**
 * 토큰 갱신 함수 (5초 캐싱 적용)
 *
 * 동시 요청 시:
 * 1. 캐시된 결과가 있으면 즉시 반환
 * 2. 진행 중인 refresh가 있으면 그 Promise를 기다림
 * 3. 둘 다 없으면 새 refresh 시작
 *
 * @param refreshToken - 현재 refresh_token
 * @param caller - 호출자 식별 (로그용: 'PROXY' | 'serverFetch')
 */
export async function refreshAccessToken(
  refreshToken: string,
  _caller: string = 'unknown'
): Promise<RefreshResult> {
  const cache = getRefreshCache();
  const now = Date.now();

  // 1. 캐시된 성공 결과가 유효하면 즉시 반환
  if (cache.result && cache.result.success && now - cache.timestamp < REFRESH_CACHE_TTL) {
    return cache.result;
  }

  // 2. 진행 중인 refresh가 있고, 아직 결과가 없으면 기다림 (실패한 결과는 캐시 안 함)
  if (cache.promise && !cache.result && now - cache.timestamp < REFRESH_CACHE_TTL) {
    return cache.promise;
  }

  // 2-1. 이전 refresh가 실패했으면 캐시 초기화
  if (cache.result && !cache.result.success) {
    cache.promise = null;
    cache.result = null;
  }

  // 3. 새 refresh 시작
  cache.timestamp = now;
  cache.result = null;

  cache.promise = doRefreshToken(refreshToken).then(result => {
    cache.result = result;
    return result;
  });

  return cache.promise;
}

/**
 * 캐시 초기화 (테스트용)
 */
export function clearRefreshCache(): void {
  const cache = getRefreshCache();
  cache.promise = null;
  cache.timestamp = 0;
  cache.result = null;
}