import axios, { type AxiosError, type InternalAxiosRequestConfig } from "axios";

const API_BASE_URL = import.meta.env.VITE_API_URL || "/api/v1";

const client = axios.create({
  baseURL: API_BASE_URL,
  headers: { "Content-Type": "application/json" },
  // Send the HttpOnly cookie automatically on every request
  withCredentials: true,
});

// ── Silent token refresh ────────────────────────────────────────────
// Prevent multiple concurrent refresh attempts
let _refreshing = false;
let _refreshWaiters: Array<(ok: boolean) => void> = [];

async function tryRefresh(): Promise<boolean> {
  if (_refreshing) {
    // Another request is already refreshing — wait for it
    return new Promise((resolve) => _refreshWaiters.push(resolve));
  }

  _refreshing = true;
  try {
    await axios.post(
      `${API_BASE_URL}/auth/refresh`,
      {},
      { withCredentials: true },
    );
    _refreshWaiters.forEach((cb) => cb(true));
    return true;
  } catch {
    _refreshWaiters.forEach((cb) => cb(false));
    return false;
  } finally {
    _refreshing = false;
    _refreshWaiters = [];
  }
}

// ── Response interceptor ────────────────────────────────────────────
client.interceptors.response.use(
  (response) => response,
  async (error: AxiosError<{ detail?: string; code?: string }>) => {
    const status = error.response?.status;
    const originalRequest = error.config as InternalAxiosRequestConfig & { _retried?: boolean };

    // On 401, attempt a silent token refresh once before redirecting
    if (status === 401 && !originalRequest?._retried) {
      // Don't attempt refresh on the refresh or login endpoints themselves
      const url = originalRequest?.url ?? "";
      if (!url.includes("/auth/refresh") && !url.includes("/auth/login")) {
        originalRequest._retried = true;
        const refreshed = await tryRefresh();
        if (refreshed) {
          // Retry the original request with the new cookie
          return client(originalRequest);
        }
      }

      // Refresh failed or not applicable — redirect to login
      if (window.location.pathname !== "/login") {
        window.location.href = "/login";
      }
    }

    // Extract error message — detail can be a string or a structured object
    const rawDetail = error.response?.data?.detail;
    const message =
      typeof rawDetail === "string"
        ? rawDetail
        : typeof rawDetail === "object" && rawDetail !== null && (rawDetail as { message?: string }).message
          ? (rawDetail as { message: string }).message
          : error.message || "An unexpected error occurred";

    const enhancedError = new Error(message) as Error & {
      status?: number;
      code?: string;
      detail?: unknown;
    };
    enhancedError.status = status;
    // Preserve the full detail object so consumers can inspect it (e.g. for modal flows)
    enhancedError.detail = rawDetail;
    enhancedError.code =
      typeof rawDetail === "object" && rawDetail !== null
        ? (rawDetail as { code?: string }).code
        : error.response?.data?.code;

    return Promise.reject(enhancedError);
  },
);

export default client;
