/**
 * 전역 Fetch Wrapper
 *
 * 모든 Server Actions에서 사용할 공통 fetch 함수
 * - 401 에러 자동 감지 및 토큰 자동 갱신
 * - 일관된 에러 처리
 * - 헤더 자동 설정
 */

import { cookies, headers } from 'next/headers';
import { redirect } from 'next/navigation';
import { isNextRedirectError } from '@/lib/utils/redirect-error';
import { createErrorResponse, type ApiErrorResponse } from './errors';
import { refreshAccessToken } from './refresh-token';

/**
 * 토큰 쿠키 삭제 (리프레시 실패 또는 인증 만료 시)
 *
 * ⚠️ 중요: redirect('/login') 호출 전에 반드시 실행해야 함
 * 쿠키가 남아있으면 미들웨어가 "인증됨"으로 판단하여 무한 루프 발생
 */
async function clearTokenCookies() {
  const cookieStore = await cookies();

  // 토큰 쿠키 삭제
  cookieStore.delete('access_token');
  cookieStore.delete('refresh_token');
  cookieStore.delete('token_refreshed_at');
  cookieStore.delete('is_authenticated');

  console.log('🗑️ [serverFetch] 토큰 쿠키 삭제 완료 (무한 루프 방지)');
}

/**
 * 새 토큰을 쿠키에 저장
 */
async function setNewTokenCookies(tokens: {
  accessToken?: string;
  refreshToken?: string;
  expiresIn?: number;
}) {
  const cookieStore = await cookies();
  const isProduction = process.env.NODE_ENV === 'production';

  if (tokens.accessToken) {
    cookieStore.set('access_token', tokens.accessToken, {
      httpOnly: true,
      secure: isProduction,
      sameSite: 'lax',
      path: '/',
      maxAge: tokens.expiresIn || 7200,
    });

    // 🔔 토큰 갱신 신호 쿠키 설정 (클라이언트에서 감지용)
    // HttpOnly: false로 설정하여 클라이언트에서 읽을 수 있게 함
    cookieStore.set('token_refreshed_at', Date.now().toString(), {
      httpOnly: false,
      secure: isProduction,
      sameSite: 'lax',
      path: '/',
      maxAge: 60, // 1분 후 자동 삭제
    });
    console.log('🔔 [setNewTokenCookies] token_refreshed_at 신호 쿠키 설정');
  }

  if (tokens.refreshToken) {
    cookieStore.set('refresh_token', tokens.refreshToken, {
      httpOnly: true,
      secure: isProduction,
      sameSite: 'lax',
      path: '/',
      maxAge: 604800, // 7 days
    });
  }
}

/**
 * API 헤더 생성 (Server Side)
 *
 * 🆕 미들웨어에서 전달한 새 토큰 우선 사용
 * - 미들웨어 pre-refresh 성공 시 request headers에 'x-refreshed-access-token' 설정
 * - Set-Cookie는 응답 헤더에만 설정되어 같은 요청 내 cookies()로 읽을 수 없음
 * - 따라서 request headers를 먼저 확인
 */
export async function getServerApiHeaders(token?: string): Promise<HeadersInit> {
  // 🆕 미들웨어에서 전달한 새 토큰 먼저 확인
  const headerStore = await headers();
  const refreshedAccessToken = headerStore.get('x-refreshed-access-token');

  const cookieStore = await cookies();
  const accessToken = token || refreshedAccessToken || cookieStore.get('access_token')?.value;

  // 디버깅: 어떤 토큰을 사용하는지 로그
  if (refreshedAccessToken) {
    console.log('🔵 [getServerApiHeaders] Using refreshed token from middleware headers');
  }

  return {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Authorization': accessToken ? `Bearer ${accessToken}` : '',
    'X-API-KEY': process.env.API_KEY || '',
  };
}

/**
 * Server Action용 Fetch Wrapper
 *
 * 🔄 토큰 갱신 로직:
 * 1. 현재 access_token으로 요청
 * 2. 401 응답 시 → refresh_token으로 새 토큰 발급
 * 3. 새 토큰으로 원래 요청 재시도
 * 4. 재시도도 실패하면 → 로그인 페이지로 리다이렉트
 *
 * @example
 * ```typescript
 * const { response, error } = await serverFetch(url, { method: 'GET' });
 * if (error) return error; // 에러 응답 반환 (클라이언트에서 처리)
 * // response 사용...
 * ```
 */
export async function serverFetch(
  url: string,
  options?: RequestInit & {
    skipAuthCheck?: boolean;
  }
): Promise<{ response: Response | null; error: ApiErrorResponse | null }> {
  try {
    // 🆕 미들웨어에서 전달한 새 refresh_token 먼저 확인
    const headerStore = await headers();
    const refreshedRefreshToken = headerStore.get('x-refreshed-refresh-token');

    const cookieStore = await cookies();
    const refreshToken = refreshedRefreshToken || cookieStore.get('refresh_token')?.value;

    const baseHeaders = await getServerApiHeaders() as Record<string, string>;

    // FormData일 경우 Content-Type을 제외 (브라우저가 자동 설정)
    const isFormData = options?.body instanceof FormData;
    const requestHeaders: HeadersInit = isFormData
      ? {
          Accept: baseHeaders.Accept,
          Authorization: baseHeaders.Authorization,
          'X-API-KEY': baseHeaders['X-API-KEY'],
        }
      : baseHeaders;

    let response = await fetch(url, {
      ...options,
      headers: {
        ...requestHeaders,
        ...options?.headers,
      },
      cache: options?.cache || 'no-store',
    });

    // 🔄 401 응답 시 토큰 갱신 후 재시도
    if (response.status === 401 && !options?.skipAuthCheck && refreshToken) {
      console.log('🔄 [serverFetch] Got 401, attempting token refresh...');

      const refreshResult = await refreshAccessToken(refreshToken, 'serverFetch');

      if (refreshResult.success && refreshResult.accessToken) {
        console.log('✅ [serverFetch] Token refreshed, retrying original request...');

        // 새 토큰을 쿠키에 저장
        await setNewTokenCookies(refreshResult);

        // 새 토큰으로 원래 요청 재시도
        const newBaseHeaders = await getServerApiHeaders(refreshResult.accessToken) as Record<string, string>;
        // FormData일 경우 Content-Type을 제외 (브라우저가 자동 설정)
        const newRequestHeaders: HeadersInit = isFormData
          ? {
              Accept: newBaseHeaders.Accept,
              Authorization: newBaseHeaders.Authorization,
              'X-API-KEY': newBaseHeaders['X-API-KEY'],
            }
          : newBaseHeaders;
        response = await fetch(url, {
          ...options,
          headers: {
            ...newRequestHeaders,
            ...options?.headers,
          },
          cache: options?.cache || 'no-store',
        });

        console.log('🔵 [serverFetch] Retry response status:', response.status);

        // 재시도도 401이면 로그인으로
        if (response.status === 401) {
          console.warn('🔴 [serverFetch] Retry failed with 401, redirecting to login...');
          await clearTokenCookies(); // ⚠️ 무한 루프 방지: 쿠키 삭제 후 redirect
          redirect('/login');
        }
      } else {
        // 리프레시 실패 → 로그인 페이지로
        console.warn('🔴 [serverFetch] Token refresh failed, redirecting to login...');
        await clearTokenCookies(); // ⚠️ 무한 루프 방지: 쿠키 삭제 후 redirect
        redirect('/login');
      }
    } else if (response.status === 401 && !options?.skipAuthCheck) {
      // refresh_token이 없는 경우
      console.warn(`[serverFetch] 401 Unauthorized (no refresh token): ${url} → 로그인 페이지로 이동`);
      await clearTokenCookies(); // ⚠️ 무한 루프 방지: 쿠키 삭제 후 redirect
      redirect('/login');
    }

    // 403 Forbidden
    if (response.status === 403) {
      console.warn(`[serverFetch] 403 Forbidden: ${url}`);
      return {
        response: null,
        error: createErrorResponse(403, '접근 권한이 없습니다.', 'FORBIDDEN'),
      };
    }

    return { response, error: null };
  } catch (error) {
    // Next.js 15: redirect()는 특수한 에러를 throw하므로 다시 throw해서 Next.js가 처리하도록 함
    if (isNextRedirectError(error)) throw error;
    console.error(`[serverFetch] Network error: ${url}`, error);
    return {
      response: null,
      error: createErrorResponse(500, '네트워크 오류가 발생했습니다.', 'NETWORK_ERROR'),
    };
  }
}

/**
 * JSON 응답 파싱 헬퍼
 */
export async function parseJsonResponse<T>(response: Response): Promise<T | null> {
  try {
    return await response.json();
  } catch {
    console.error('[parseJsonResponse] JSON 파싱 실패');
    return null;
  }
}

/**
 * 전체 API 호출 헬퍼 (fetch + JSON 파싱)
 *
 * @example
 * ```typescript
 * const { data, error } = await serverApiCall<UserResponse>(url, { method: 'GET' });
 * if (error) return error;
 * return data;
 * ```
 */
export async function serverApiCall<T>(
  url: string,
  options?: RequestInit
): Promise<{ data: T | null; error: ApiErrorResponse | null }> {
  const { response, error } = await serverFetch(url, options);

  if (error || !response) {
    return { data: null, error };
  }

  if (!response.ok) {
    const errorData = await parseJsonResponse<{ message?: string; code?: string }>(response);
    return {
      data: null,
      error: createErrorResponse(
        response.status,
        errorData?.message || '요청 처리 중 오류가 발생했습니다.',
        errorData?.code
      ),
    };
  }

  // 204 No Content
  if (response.status === 204) {
    return { data: null, error: null };
  }

  const data = await parseJsonResponse<T>(response);
  return { data, error: null };
}