import axios, {
  AxiosError,
  type AxiosInstance,
  type InternalAxiosRequestConfig,
} from "axios";
import {
  clearAuth,
  getAccessToken,
  getRefreshToken,
  setTokens,
} from "@/store/authStore";
import type { TokenResponse } from "@/types/auth.types";

// All calls go through "/api"; nginx (prod) and the Vite dev proxy forward it to the API.
export const api: AxiosInstance = axios.create({
  baseURL: "/api",
  headers: { "Content-Type": "application/json" },
});

// A bare client for the refresh call so it never triggers the response interceptor (no loops).
const refreshClient = axios.create({
  baseURL: "/api",
  headers: { "Content-Type": "application/json" },
});

api.interceptors.request.use((config: InternalAxiosRequestConfig) => {
  const token = getAccessToken();
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// --- Silent refresh handling -------------------------------------------------
// While a refresh is in flight, queue concurrent 401s and replay them once done.
let isRefreshing = false;
let pendingQueue: Array<(token: string | null) => void> = [];

function flushQueue(token: string | null): void {
  pendingQueue.forEach((resolve) => resolve(token));
  pendingQueue = [];
}

function redirectToLogin(): void {
  clearAuth();
  const path = window.location.pathname;
  if (path !== "/login" && path !== "/register") {
    window.location.assign("/login");
  }
}

async function performRefresh(): Promise<string | null> {
  const refreshToken = getRefreshToken();
  if (!refreshToken) {
    return null;
  }

  try {
    const { data } = await refreshClient.post<TokenResponse>("/auth/refresh", {
      refreshToken,
    });
    setTokens(data);
    return data.accessToken;
  } catch {
    return null;
  }
}

api.interceptors.response.use(
  (response) => response,
  async (error: AxiosError) => {
    const original = error.config as
      | (InternalAxiosRequestConfig & { _retry?: boolean })
      | undefined;

    const status = error.response?.status;
    const isAuthEndpoint =
      original?.url?.includes("/auth/login") ||
      original?.url?.includes("/auth/register") ||
      original?.url?.includes("/auth/refresh");

    if (status !== 401 || !original || original._retry || isAuthEndpoint) {
      return Promise.reject(error);
    }

    original._retry = true;

    if (isRefreshing) {
      // Wait for the in-flight refresh, then retry (or fail) with its result.
      return new Promise((resolve, reject) => {
        pendingQueue.push((token) => {
          if (!token) {
            reject(error);
            return;
          }
          original.headers.Authorization = `Bearer ${token}`;
          resolve(api(original));
        });
      });
    }

    isRefreshing = true;
    const newToken = await performRefresh();
    isRefreshing = false;
    flushQueue(newToken);

    if (!newToken) {
      redirectToLogin();
      return Promise.reject(error);
    }

    original.headers.Authorization = `Bearer ${newToken}`;
    return api(original);
  },
);

/** Extracts a user-facing message from an API error response. */
const GENERIC_API_TITLES = new Set([
  "An unexpected error occurred.",
  "Internal Server Error",
]);

export function extractApiError(error: unknown, fallback = "Something went wrong."): string {
  if (axios.isAxiosError(error)) {
    const data = error.response?.data as { error?: string; title?: string } | undefined;
    if (data?.error) return data.error;
    if (data?.title && !GENERIC_API_TITLES.has(data.title)) return data.title;
    return fallback;
  }
  if (error instanceof Error) {
    return error.message;
  }
  return fallback;
}
