const API_URL = process.env.NEXT_PUBLIC_API_URL || '/api/v1';

export interface ApiResponse<T = any> {
  success: boolean;
  data: T;
  message?: string;
  code?: string;
  requestId?: string;
  pagination?: {
    total: number;
    page: number;
    pageSize: number;
    totalPages: number;
  };
}

/** Cookie-only auth — do not persist tokens in localStorage (XSS surface). */
export function clearTokens(): void {
  if (typeof window === 'undefined') return;
  localStorage.removeItem('my11s_access_token');
  localStorage.removeItem('my11s_refresh_token');
  localStorage.removeItem('my11s_user');
}

export function getCurrentUser(): any {
  if (typeof window === 'undefined') return null;
  try {
    const user = localStorage.getItem('my11s_user');
    return user ? JSON.parse(user) : null;
  } catch {
    return null;
  }
}

export function setCurrentUser(user: any): void {
  if (typeof window === 'undefined') return;
  localStorage.setItem('my11s_user', JSON.stringify(user));
}

/** @deprecated no-op — tokens live in httpOnly cookies only */
export function setTokens(_accessToken?: string, _refreshToken?: string): void {
  if (typeof window === 'undefined') return;
  localStorage.removeItem('my11s_access_token');
  localStorage.removeItem('my11s_refresh_token');
}

export function getAccessToken(): string | null {
  return null;
}

export function getRefreshToken(): string | null {
  return null;
}

export function idempotencyKey(prefix = 'req'): string {
  if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {
    return `${prefix}-${crypto.randomUUID()}`;
  }
  return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 12)}`;
}

let isRefreshing = false;
let failedQueue: Array<{ resolve: (ok: boolean) => void; reject: (err: any) => void }> = [];

function processQueue(error: any, ok = false) {
  failedQueue.forEach((p) => (error ? p.reject(error) : p.resolve(ok)));
  failedQueue = [];
}

export async function apiRequest<T = any>(
  endpoint: string,
  options: RequestInit & { idempotencyKey?: string; timeoutMs?: number } = {}
): Promise<ApiResponse<T>> {
  const url = `${API_URL}${endpoint.startsWith('/') ? endpoint : `/${endpoint}`}`;
  const headers: Record<string, string> = {
    'Content-Type': 'application/json',
    ...(options.headers as Record<string, string>),
  };

  if (options.idempotencyKey) headers['X-Idempotency-Key'] = options.idempotencyKey;

  if (typeof document !== 'undefined') {
    const csrf = document.cookie
      .split('; ')
      .find((c) => c.startsWith('my11s_csrf='))
      ?.split('=')[1];
    if (csrf) headers['X-CSRF-Token'] = csrf;
  }

  const timeoutMs = options.timeoutMs ?? 12_000;
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
  const { timeoutMs: _t, idempotencyKey: _k, signal: userSignal, ...fetchOpts } = options as any;

  if (userSignal) {
    if (userSignal.aborted) controller.abort();
    else userSignal.addEventListener('abort', () => controller.abort(), { once: true });
  }

  const doFetch = (signal?: AbortSignal) =>
    fetch(url, {
      ...fetchOpts,
      headers,
      credentials: 'include',
      signal: signal || controller.signal,
    });

  let response: Response;
  try {
    response = await doFetch();
  } catch (err: any) {
    clearTimeout(timeoutId);
    if (err?.name === 'AbortError') {
      const error: any = new Error('Request timed out. Please try again.');
      error.code = 'TIMEOUT';
      throw error;
    }
    throw err;
  }
  clearTimeout(timeoutId);

  if (response.status === 401 && !endpoint.includes('/auth/login') && !endpoint.includes('/auth/register')) {
    if (isRefreshing) {
      await new Promise<boolean>((resolve, reject) => failedQueue.push({ resolve, reject }));
      response = await doFetch();
    } else {
      isRefreshing = true;
      try {
        const refreshResponse = await fetch(`${API_URL}/auth/refresh`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            ...(headers['X-CSRF-Token'] ? { 'X-CSRF-Token': headers['X-CSRF-Token'] } : {}),
          },
          credentials: 'include',
          body: JSON.stringify({}),
        });
        const refreshData = await refreshResponse.json();
        if (refreshResponse.ok && refreshData.success) {
          processQueue(null, true);
          response = await doFetch();
        } else {
          processQueue(new Error('Session expired'), false);
          clearTokens();
          if (typeof window !== 'undefined') {
            const path = window.location.pathname;
            const isStaffArea =
              path.startsWith('/admin') || path.startsWith('/scorer') || path.startsWith('/ops');
            const dest = isStaffArea ? '/ops/login' : '/login';
            if (!path.startsWith(dest)) {
              window.location.href = dest;
            }
          }
          throw new Error('Session expired. Please log in again.');
        }
      } catch (err) {
        processQueue(err, false);
        clearTokens();
        throw err;
      } finally {
        isRefreshing = false;
      }
    }
  }

  const data = await response.json().catch(() => ({}));
  if (!response.ok || data.success === false) {
    const error: any = new Error(data.message || `API error: ${response.status}`);
    error.code = data.code;
    error.status = response.status;
    error.details = data.data;
    throw error;
  }

  return data;
}

/** Multipart upload (do not set JSON Content-Type). */
export async function apiUpload<T = any>(
  endpoint: string,
  formData: FormData
): Promise<ApiResponse<T>> {
  const url = `${API_URL}${endpoint.startsWith('/') ? endpoint : `/${endpoint}`}`;
  const headers: Record<string, string> = {};

  if (typeof document !== 'undefined') {
    const csrf = document.cookie
      .split('; ')
      .find((c) => c.startsWith('my11s_csrf='))
      ?.split('=')[1];
    if (csrf) headers['X-CSRF-Token'] = csrf;
  }

  const response = await fetch(url, {
    method: 'POST',
    headers,
    body: formData,
    credentials: 'include',
  });

  const data = await response.json().catch(() => ({}));
  if (!response.ok || data.success === false) {
    const error: any = new Error(data.message || `Upload failed: ${response.status}`);
    error.code = data.code;
    error.status = response.status;
    throw error;
  }
  return data;
}
