import axios, { type AxiosError, type InternalAxiosRequestConfig } from "axios";
import { useAuthStore } from "@modules/auth/store/authStore";
import { getApiBaseUrl } from "./config";

const apiBaseUrl = getApiBaseUrl();

export const apiClient = axios.create({
  baseURL: apiBaseUrl,
  timeout: 10_000
});

export const authClient = axios.create({
  baseURL: apiBaseUrl,
  timeout: 10_000,
  withCredentials: true
});
let getAccessToken: () => string | null = () => null;
let refreshPromise: Promise<string | null> | null = null;

export function setAccessTokenGetter(getter: () => string | null): void {
  getAccessToken = getter;
}

export function applyAuthHeader(headers: Record<string, string>): Record<string, string> {
  const token = getAccessToken();
  if (token) {
    return { ...headers, Authorization: `Bearer ${token}` };
  }
  return headers;
}

export async function bootstrapSessionRefresh(): Promise<string | null> {
  return refreshAccessToken();
}

async function refreshAccessToken(): Promise<string | null> {
  if (!refreshPromise) {
    refreshPromise = authClient
      .post("/api/v1/auth/refresh")
      .then((response) => {
        const { access_token: accessToken, user } = response.data;
        useAuthStore.getState().setSession(accessToken, user);
        return accessToken as string;
      })
      .catch(() => {
        useAuthStore.getState().clearSession();
        return null;
      })
      .finally(() => {
        refreshPromise = null;
      });
  }
  return refreshPromise;
}

apiClient.interceptors.request.use((config) => {
  config.headers = applyAuthHeader(config.headers as Record<string, string>) as typeof config.headers;
  return config;
});

apiClient.interceptors.response.use(
  (response) => response,
  async (error: AxiosError) => {
    const detail =
      error.response && typeof error.response.data === "object" && error.response.data !== null
        ? (error.response.data as { detail?: string }).detail
        : undefined;
    if (
      error.response?.status === 403 &&
      (detail === "ACCOUNT_BLOCKED" || detail === "EMAIL_NOT_VERIFIED")
    ) {
      useAuthStore.getState().clearSession();
      return Promise.reject(error);
    }
    if (error.response?.status === 401 && detail === "TOKEN_REVOKED") {
      useAuthStore.getState().clearSession();
      return Promise.reject(error);
    }
    const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean };
    if (error.response?.status !== 401 || !originalRequest || originalRequest._retry) {
      return Promise.reject(error);
    }
    if (originalRequest.url?.includes("/api/v1/auth/")) {
      return Promise.reject(error);
    }

    originalRequest._retry = true;
    const accessToken = await refreshAccessToken();
    if (!accessToken) {
      return Promise.reject(error);
    }

    originalRequest.headers = applyAuthHeader(
      originalRequest.headers as Record<string, string>
    ) as typeof originalRequest.headers;
    return apiClient.request(originalRequest);
  }
);
