import { UseQueryOptions, QueryObserverOptions } from "@tanstack/react-query";
import axios, { AxiosError, InternalAxiosRequestConfig } from "axios";
import { URL_CONSTANTS } from "@/constants/URLS";
import { API_BASE_URL } from "@/constants/apiConfig";

const client = axios.create({
  baseURL: API_BASE_URL,
});

function getCookie(name: string): string | undefined {
  return document.cookie
    .split("; ")
    .find((row) => row.startsWith(`${name}=`))
    ?.split("=")[1];
}

function setCookie(name: string, value: string, days: number) {
  const expires = new Date();
  expires.setDate(expires.getDate() + days);
  document.cookie = `${name}=${value}; path=/; expires=${expires.toUTCString()}; SameSite=Lax`;
}

function clearAuthCookies() {
  [
    "auth-token",
    "refresh-token",
    "auth-user",
    "current-position-id",
    "selected-position-id",
  ].forEach((name) => {
    document.cookie = `${name}=; Max-Age=0; path=/`;
  });
}

// Attach auth token to every request
client.interceptors.request.use((config) => {
  const token = getCookie("auth-token");
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// Token refresh state
let isRefreshing = false;
let failedQueue: {
  resolve: (token: string) => void;
  reject: (error: unknown) => void;
}[] = [];

function processQueue(error: unknown, token?: string) {
  failedQueue.forEach(({ resolve, reject }) => {
    if (error) {
      reject(error);
    } else {
      resolve(token!);
    }
  });
  failedQueue = [];
}

// Handle auth errors globally with token refresh
client.interceptors.response.use(
  (response) => response,
  async (error: AxiosError) => {
    const originalRequest = error.config as InternalAxiosRequestConfig & {
      _retry?: boolean;
    };

    // Don't intercept if:
    // - no response (network error)
    // - status is not 401
    // - already retried
    // - it's the refresh endpoint itself
    if (
      !error.response ||
      error.response.status !== 401 ||
      originalRequest._retry ||
      originalRequest.url === URL_CONSTANTS.AUTH.REFRESH_TOKEN
    ) {
      return Promise.reject(error);
    }

    if (isRefreshing) {
      return new Promise<string>((resolve, reject) => {
        failedQueue.push({ resolve, reject });
      }).then((token) => {
        originalRequest.headers.Authorization = `Bearer ${token}`;
        return client(originalRequest);
      });
    }

    originalRequest._retry = true;
    isRefreshing = true;

    const refreshToken = getCookie("refresh-token");

    if (!refreshToken) {
      isRefreshing = false;
      clearAuthCookies();
      return Promise.reject(error);
    }

    try {
      const { data } = await client.post<{
        data: { token: string; refreshToken: string };
      }>(URL_CONSTANTS.AUTH.REFRESH_TOKEN, { refreshToken });
      const { token, refreshToken: newRefreshToken } = data.data;
      setCookie("auth-token", token, 7);
      setCookie("refresh-token", newRefreshToken, 7);
      originalRequest.headers.Authorization = `Bearer ${token}`;
      processQueue(null, token);
      return client(originalRequest);
    } catch (refreshError) {
      processQueue(refreshError, undefined);
      clearAuthCookies();
      return Promise.reject(refreshError);
    } finally {
      isRefreshing = false;
    }
  },
);

export { client };
export type { UseQueryOptions, QueryObserverOptions };
