// src/api/client.ts
import axios, { type AxiosRequestConfig, type AxiosResponse } from "axios";

const API_URL = `${import.meta.env.VITE_BACKEND_URL}/api`;

const apiClient = axios.create({
  baseURL: API_URL,
  withCredentials: true,
});

const publicClient = axios.create({
  baseURL: API_URL,
  withCredentials: true,
});

// =========================
// CSRF for public client
// =========================
publicClient.interceptors.request.use((config) => {
  const getCookie = (name: string) =>
    document.cookie
      .split("; ")
      .find((row) => row.startsWith(name + "="))
      ?.split("=")[1] ?? null;

  const token = getCookie("csrftoken");
  const method = (config.method || "").toLowerCase();

  if (token && ["post", "put", "patch", "delete"].includes(method)) {
    if (!config.headers) config.headers = {} as import("axios").AxiosRequestHeaders;
    config.headers["X-CSRFToken"] = token;
  }

  if (config.headers && "Authorization" in config.headers) {
    delete config.headers.Authorization;
  }

  return config;
});

// =========================
// TOKEN REFRESH LOGIC
// =========================
let isRefreshing = false;
let failedQueue: { resolve: () => void; reject: (err?: any) => void }[] = [];

const processQueue = (error: any) => {
  failedQueue.forEach((prom) => {
    if (error) prom.reject(error);
    else prom.resolve();
  });
  failedQueue = [];
};

// Response interceptor for 401
apiClient.interceptors.response.use(
  (response: AxiosResponse) => response,
  async (error) => {
    const originalRequest = error.config;

    if (!error.response) {
      alert("Network error, please check your connection.");
      return Promise.reject(error);
    }

    const status = error.response.status;

    // Handle 401 separately (token refresh)
    if (status === 401) {
      if (originalRequest._retry) {
        window.location.href = "/login";
        return Promise.reject(error);
      }

      if (isRefreshing) {
        return new Promise<void>((resolve, reject) => {
          failedQueue.push({ resolve, reject });
        })
          .then(() => apiClient(originalRequest))
          .catch((err) => Promise.reject(err));
      }

      originalRequest._retry = true;
      isRefreshing = true;

      try {
        await ApiClient.refreshToken();
        processQueue(null);
        return apiClient(originalRequest);

      } catch (err) {
        processQueue(err);
        window.location.href = "/login";
        return Promise.reject(err);
        
      } finally {
        isRefreshing = false;
      }
    }

    // Central error handling for other status codes
    if (status >= 400 && status < 500) {
      alert(
        error.response.data?.detail ||
          `Client error ${status}: ${JSON.stringify(error.response.data)}`
      );
    } else if (status >= 500) {
      // Optionally redirect to a generic error page
      // window.location.href = "/error";
      alert("Server error, please try again later.");
    }

    return Promise.reject(error);
  }
);

// =========================
// ApiClient object
// =========================
const ApiClient = {
  login: async (username: string, password: string) => {
    await publicClient.post("/account/token/", { username, password });
  },

  logout: async () => {
    await publicClient.post("/account/logout/");
  },

  refreshToken: async () => {
    await publicClient.post("/account/token/refresh/");
  },

  fetchCurrentUser: async (): Promise<any> => {
    const res = await apiClient.get("/account/user/me/");
    return res.data;
  },

  checkAuth: async (): Promise<boolean> => {
    try {
      await ApiClient.fetchCurrentUser();
      return true;
    } catch {
      return false;
    }
  },

  request: async <T = any>(
    method: "get" | "post" | "put" | "patch" | "delete",
    endpoint: string,
    data?: any,
    config: AxiosRequestConfig = {}
  ): Promise<T> => {
    const url = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
    const response = await apiClient({
      method,
      url,
      data: ["post", "put", "patch"].includes(method) ? data : undefined,
      params: ["get", "delete"].includes(method) ? data : undefined,
      ...config,
    });
    return response.data;
  },
};

export default ApiClient;
