/**
 * API 클라이언트 — JWT access/refresh 자동 갱신 (PRD §3.5.5, C-06)
 */
import { useAuthStore } from '../store/auth';

console.log('[API] api.ts loaded');

const BASE_URL = import.meta.env.VITE_API_BASE_URL ?? '';

const TOKEN_KEYS = {
  access: 'pcnt_access_token',
  refresh: 'pcnt_refresh_token',
};

export function getStoredTokens() {
   const access = localStorage.getItem(TOKEN_KEYS.access);
   const refresh = localStorage.getItem(TOKEN_KEYS.refresh);
   console.log('[API] getStoredTokens: access present=', !!access, 'refresh present=', !!refresh);
   return {
     access,
     refresh,
   };
 }

export function storeTokens(access: string, refresh?: string) {
   console.log('[API] storeTokens: saving access length', access?.length, 'refresh length', refresh?.length);
   localStorage.setItem(TOKEN_KEYS.access, access);
   if (refresh) localStorage.setItem(TOKEN_KEYS.refresh, refresh);
 }

export function clearTokens() {
  localStorage.removeItem(TOKEN_KEYS.access);
  localStorage.removeItem(TOKEN_KEYS.refresh);
}

export class ApiError extends Error {
  code?: string;
  status: number;
  constructor(message: string, status: number, code?: string) {
    super(message);
    this.status = status;
    this.code = code;
  }
}

type Options = RequestInit & { json?: unknown };

async function rawRequest(path: string, opts: Options, withAuth: boolean): Promise<Response> {
   const headers = new Headers(opts.headers);
   headers.set('Content-Type', 'application/json');
   if (withAuth) {
     const { access } = getStoredTokens();
     const token = access?.trim();
     if (token) {
       headers.set('Authorization', `Bearer ${token}`);
       console.log('[API] Setting Authorization header with token:', token.substring(0, 10) + '...');
     } else {
       console.log('[API] No access token found');
     }
   }
   console.log('[API] Request headers:', Object.fromEntries(headers.entries()));
   const body = opts.json !== undefined ? JSON.stringify(opts.json) : opts.body;
   return fetch(`${BASE_URL}${path}`, { ...opts, headers, body });
 }

let refreshing: Promise<boolean> | null = null;

async function refreshAccess(): Promise<boolean> {
   if (refreshing) return refreshing;
   refreshing = (async () => {
     const { refresh } = getStoredTokens();
     const token = refresh?.trim();
     if (!token) return false;
     try {
       const res = await fetch(`${BASE_URL}/api/auth/refresh`, {
         method: 'POST',
         headers: { 'Content-Type': 'application/json' },
         body: JSON.stringify({ refreshToken: token }),
       });
       if (!res.ok) return false;
       const data = await res.json();
       storeTokens(data.accessToken);
       console.log('[API] Refreshed access token:', data.accessToken?.substring(0,10) + '...');
       return true;
     } catch {
       return false;
     } finally {
       refreshing = null;
     }
   })();
   return refreshing;
 }

export async function api<T>(
   path: string,
   opts: Options = {},
   withAuth = true,
 ): Promise<T> {
   let res = await rawRequest(path, opts, withAuth);
   console.log('[API] Response status:', res.status, 'for', path);
   if (res.status === 401 && withAuth) {
     console.log('[API] 401 received, attempting refresh');
     const ok = await refreshAccess();
     if (ok) {
       console.log('[API] Refresh succeeded, retrying request');
       res = await rawRequest(path, opts, withAuth);
     } else {
       console.log('[API] Refresh failed, logging out');
       useAuthStore.getState().logout();
     }
   }
   if (!res.ok) {
     let message = '요청에 실패했습니다.';
     let code: string | undefined;
     try {
       const body = await res.json();
       if (body?.error) message = body.error;
       if (body?.code) code = body.code;
     } catch {
       /* non-json */
     }
     throw new ApiError(message, res.status, code);
   }
   if (res.status === 204) return undefined as T;
   return res.json() as Promise<T>;
 }

export const http = {
  get: <T>(path: string) => api<T>(path),
  post: <T>(path: string, json?: unknown) => api<T>(path, { method: 'POST', json }),
  put: <T>(path: string, json?: unknown) => api<T>(path, { method: 'PUT', json }),
};
