// lib/api/client.ts
import { AUTH_CONFIG } from './auth/auth-config';
import type { AuthMode } from './auth/types';

interface ClientConfig {
  mode: AuthMode;
  apiKey?: string;                    // API Key 모드용
  getToken?: () => string | null;     // Bearer 모드용
}

interface ApiErrorResponse {
  message: string;
  errors?: Record<string, string[]>;
  code?: string;
}

export class ApiClient {
  private baseURL: string;
  private mode: AuthMode;
  private apiKey?: string;
  private getToken?: () => string | null;

  constructor(config: ClientConfig) {
    this.baseURL = AUTH_CONFIG.apiUrl;
    this.mode = config.mode;
    this.apiKey = config.apiKey;
    this.getToken = config.getToken;
  }

  /**
   * 인증 헤더 생성
   */
  private getAuthHeaders(): Record<string, string> {
    const headers: Record<string, string> = {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
    };

    // API Key는 모든 모드에서 기본으로 포함 (PHP API 요구사항)
    if (this.apiKey) {
      headers['X-API-KEY'] = this.apiKey;
    }

    switch (this.mode) {
      case 'api-key':
        // API Key만 사용 (이미 위에서 추가됨)
        break;

      case 'bearer': {
        const token = this.getToken?.();
        if (token) {
          headers['Authorization'] = `Bearer ${token}`;
        }
        // API Key도 함께 전송 (이미 위에서 추가됨)
        break;
      }

      case 'sanctum':
        // 쿠키 기반 - 별도 헤더 불필요
        break;
    }

    return headers;
  }

  /**
   * HTTP 요청 실행
   */
  async request<T>(
    endpoint: string,
    options?: RequestInit
  ): Promise<T> {
    const url = `${this.baseURL}${endpoint}`;
    const headers = {
      ...this.getAuthHeaders(),
      ...options?.headers,
    };

    const config: RequestInit = {
      ...options,
      headers,
    };

    // Sanctum 모드는 쿠키 포함
    if (this.mode === 'sanctum') {
      config.credentials = 'include';
    }

    const response = await fetch(url, config);

    if (!response.ok) {
      await this.handleError(response);
    }

    // 204 No Content 처리
    if (response.status === 204) {
      return undefined as T;
    }

    return await response.json();
  }

  /**
   * GET 요청
   * @param endpoint API 엔드포인트
   * @param options 쿼리 파라미터 등 옵션
   */
  async get<T>(endpoint: string, options?: { params?: Record<string, string> }): Promise<T> {
    let url = endpoint;
    if (options?.params) {
      const searchParams = new URLSearchParams(options.params);
      url = `${endpoint}?${searchParams.toString()}`;
    }
    return this.request<T>(url, { method: 'GET' });
  }

  /**
   * POST 요청
   */
  async post<T>(endpoint: string, data?: unknown): Promise<T> {
    return this.request<T>(endpoint, {
      method: 'POST',
      body: data ? JSON.stringify(data) : undefined,
    });
  }

  /**
   * PUT 요청
   */
  async put<T>(endpoint: string, data?: unknown): Promise<T> {
    return this.request<T>(endpoint, {
      method: 'PUT',
      body: data ? JSON.stringify(data) : undefined,
    });
  }

  /**
   * PATCH 요청
   */
  async patch<T>(endpoint: string, data?: unknown): Promise<T> {
    return this.request<T>(endpoint, {
      method: 'PATCH',
      body: data ? JSON.stringify(data) : undefined,
    });
  }

  /**
   * DELETE 요청
   * @param endpoint API 엔드포인트
   * @param options body 데이터 (일괄 삭제 등에서 사용)
   */
  async delete<T>(endpoint: string, options?: { data?: unknown }): Promise<T> {
    return this.request<T>(endpoint, {
      method: 'DELETE',
      body: options?.data ? JSON.stringify(options.data) : undefined,
    });
  }

  /**
   * 에러 처리 (자동 토큰 갱신 포함)
   */
  private async handleError(response: Response): Promise<never> {
    const data = await response.json().catch(() => ({}));

    // 401 Unauthorized - Try token refresh
    if (response.status === 401) {
      console.warn('⚠️ 401 Unauthorized - Token may be expired');

      // Client-side: Suggest token refresh to caller
      throw {
        status: 401,
        message: 'Unauthorized - Token expired',
        needsTokenRefresh: true,
        errors: data.errors,
        code: data.code,
      };
    }

    const error: ApiErrorResponse = {
      message: data.message || 'An error occurred',
      errors: data.errors || data.error?.details,
      code: data.code,
    };

    throw {
      status: response.status,
      ...error,
    };
  }
}

/**
 * Helper function to handle API calls with automatic token refresh
 *
 * Usage:
 * ```typescript
 * const data = await withTokenRefresh(() => apiClient.get('/protected'));
 * ```
 */
export async function withTokenRefresh<T>(
  apiCall: () => Promise<T>,
  maxRetries: number = 1
): Promise<T> {
  try {
    return await apiCall();
  } catch (error: unknown) {
    const apiError = error as { status?: number; needsTokenRefresh?: boolean };

    // If 401 and token refresh needed, try refreshing
    if (apiError.status === 401 && apiError.needsTokenRefresh && maxRetries > 0) {

      // Call refresh endpoint
      const refreshResponse = await fetch('/api/auth/refresh', {
        method: 'POST',
        credentials: 'include',
      });

      if (refreshResponse.ok) {
        // Retry the original API call
        return withTokenRefresh(apiCall, maxRetries - 1);
      } else {
        console.error('❌ Token refresh failed - clearing cookies and redirecting to login');
        // ⚠️ 무한 루프 방지: 쿠키 삭제 API 호출 후 redirect
        // 쿠키가 남아있으면 미들웨어가 "인증됨"으로 판단하여 무한 루프 발생
        if (typeof window !== 'undefined') {
          try {
            await fetch('/api/auth/logout', { method: 'POST' });
          } catch {
            // 로그아웃 API 실패해도 redirect 진행
          }
          window.location.href = '/login';
        }
      }
    }

    // Re-throw error if not handled
    throw error;
  }
}