/**
 * API Client wrapper for admin pages
 * Provides axios instance compatible with the existing API pattern
 *
 * Features:
 * - Automatic snake_case to camelCase conversion for responses
 * - JWT token injection from localStorage auth store
 */

import axios from 'axios';
import {
  clearStoredSession,
  getAccessToken,
  getStoredSession,
  setStoredSession,
} from '@/lib/auth/tokenManager';

const baseURL = process.env.NEXT_PUBLIC_API_URL ?? '';
const REFRESH_ENDPOINT = '/api/v1/auth/refresh';
const LOGIN_ENDPOINT = '/api/v1/auth/login';
const REGISTER_ENDPOINT = '/api/v1/auth/register';
const LOGOUT_ENDPOINT = '/api/v1/auth/logout';

let refreshPromise: Promise<string | null> | null = null;

declare module 'axios' {
  interface InternalAxiosRequestConfig {
    _retry?: boolean;
    skipAuthRefresh?: boolean;
  }
}

/**
 * Convert snake_case to camelCase
 */
function toCamelCase(str: string): string {
  return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
}

/**
 * Recursively convert object keys from snake_case to camelCase
 */
function convertToCamelCase<T>(obj: unknown): T {
  if (obj === null || obj === undefined) {
    return obj as T;
  }

  if (Array.isArray(obj)) {
    return obj.map((item) => convertToCamelCase(item)) as T;
  }

  if (typeof obj === 'object') {
    const result: Record<string, unknown> = {};
    for (const [key, value] of Object.entries(obj)) {
      const camelKey = toCamelCase(key);
      result[camelKey] = convertToCamelCase(value);
    }
    return result as T;
  }

  return obj as T;
}

export const api = axios.create({
  baseURL,
  headers: {
    'Content-Type': 'application/json',
  },
});

// Request interceptor: inject JWT token
api.interceptors.request.use(
  (config) => {
    const token = getAccessToken();
    if (token && !config.headers.Authorization) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

async function refreshAccessToken(): Promise<string | null> {
  const session = getStoredSession();
  if (!session.refreshToken) {
    return null;
  }

  const response = await axios.post(
    `${baseURL}${REFRESH_ENDPOINT}`,
    { refreshToken: session.refreshToken },
    {
      headers: {
        'Content-Type': 'application/json',
      },
    }
  );

  const payload = convertToCamelCase<{
    data?: {
      accessToken?: string;
      refreshToken?: string;
      expiresIn?: number;
    };
    accessToken?: string;
    refreshToken?: string;
    expiresIn?: number;
  }>(response.data);
  const sessionData = payload.data ?? payload;
  const accessToken = sessionData.accessToken ?? null;

  if (!accessToken) {
    return null;
  }

  setStoredSession({
    accessToken,
    refreshToken: sessionData.refreshToken ?? session.refreshToken,
    expiresAt: sessionData.expiresIn ? Date.now() + sessionData.expiresIn * 1000 : session.expiresAt,
  });

  return accessToken;
}

function shouldSkipRefresh(url?: string): boolean {
  if (!url) {
    return false;
  }

  return [REFRESH_ENDPOINT, LOGIN_ENDPOINT, REGISTER_ENDPOINT, LOGOUT_ENDPOINT].some((path) => url.includes(path));
}

async function handleUnauthorizedRedirect(): Promise<void> {
  clearStoredSession();

  if (typeof window === 'undefined') {
    return;
  }

  if (window.location.pathname !== '/login') {
    window.location.href = '/login';
  }
}

// Response interceptor: convert snake_case to camelCase
api.interceptors.response.use(
  (response) => {
    if (response.data) {
      response.data = convertToCamelCase(response.data);
    }
    return response;
  },
  async (error) => {
    if (error.response?.data) {
      error.response.data = convertToCamelCase(error.response.data);
    }

    const originalRequest = error.config;
    const isUnauthorized = error.response?.status === 401;

    if (
      !isUnauthorized ||
      !originalRequest ||
      originalRequest._retry ||
      originalRequest.skipAuthRefresh ||
      shouldSkipRefresh(originalRequest.url)
    ) {
      return Promise.reject(error);
    }

    originalRequest._retry = true;

    try {
      refreshPromise ??= refreshAccessToken().finally(() => {
        refreshPromise = null;
      });

      const refreshedAccessToken = await refreshPromise;
      if (!refreshedAccessToken) {
        await handleUnauthorizedRedirect();
        return Promise.reject(error);
      }

      originalRequest.headers.Authorization = `Bearer ${refreshedAccessToken}`;
      return api(originalRequest);
    } catch (refreshError) {
      await handleUnauthorizedRedirect();
      return Promise.reject(refreshError);
    }
  }
);
