import axios, { AxiosError, InternalAxiosRequestConfig } from "axios";
import { ErrorApiResponse, SuccessApiResponse } from "../../types/api-response";
import { tokenService } from "../../utils/tokenService";

type APP_TYPES = "development" | "staging" | "production";

const APP_MODE = process.env.NEXT_PUBLIC_APP_MODE;

const URL: Record<APP_TYPES, string> = {
  development: process.env.NEXT_PUBLIC_BACKEND_API_URL_DEV,
  staging: process.env.NEXT_PUBLIC_BACKEND_API_URL_STAGING,
  production: process.env.NEXT_PUBLIC_BACKEND_API_URL_PROD,
};

export const webApi = axios.create({
  baseURL: URL[APP_MODE],
  headers: { "Content-Type": "application/json" },
  withCredentials: true,
});

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

interface QueuedRequest {
  resolve: (value?: unknown) => void;
  reject: (reason?: unknown) => void;
}

let isRefreshing = false;
let failedQueue: QueuedRequest[] = [];

function processQueue(error: unknown, newToken: string | null = null): void {
  failedQueue.forEach(({ resolve, reject }) =>
    error ? reject(error) : resolve(newToken),
  );
  failedQueue = [];
}

const SKIP_REFRESH_URLS = [
  "/auth/refresh-token",
  "/auth/login",
  "/auth/register",
  "/auth/logout",
  "/auth/verify-email",
];

function shouldSkipRefresh(url: string | undefined): boolean {
  return SKIP_REFRESH_URLS.some((skipUrl) => url?.includes(skipUrl));
}

function forceLogout(): void {
  tokenService.clear();
  window.location.href = "/";
}

// ─── Response interceptor ─────────────────────────────────────────────────────

webApi.interceptors.response.use(
  (response) => response,

  async (error: AxiosError<ErrorApiResponse<Record<string, string[]>>>) => {
    const originalRequest = error.config as InternalAxiosRequestConfig & {
      _retry?: boolean;
    };

    // Non-401 → pass through immediately
    if (error.response?.status !== 401) {
      return Promise.reject(error);
    }

    // Auth endpoints → never refresh, just reject.
    // For logout/refresh specifically, also clear tokens since session is dead.
    if (shouldSkipRefresh(originalRequest.url)) {
      if (
        originalRequest.url?.includes("/auth/refresh-token") ||
        originalRequest.url?.includes("/auth/logout")
      ) {
        // forceLogout();
      }
      return Promise.reject(error);
    }

    // Already retried this exact request once → give up, don't loop
    if (originalRequest._retry) {
      // forceLogout();
      return Promise.reject(error);
    }

    // Another refresh already in flight → queue and wait
    if (isRefreshing) {
      return new Promise((resolve, reject) => {
        failedQueue.push({ resolve, reject });
      }).then((newToken) => {
        if (originalRequest.headers) {
          originalRequest.headers.Authorization = `Bearer ${newToken}`;
        }
        return webApi(originalRequest);
      });
    }

    // First 401 on a protected route → kick off refresh
    originalRequest._retry = true;
    isRefreshing = true;

    try {
      const refreshToken = tokenService.getRefreshToken();

      const { data } = await webApi.post<
        SuccessApiResponse<{
          accessToken: string;
          refreshToken?: string;
        }>
      >("/auth/refresh-token", { refreshToken });

      if (data.data.refreshToken) {
        tokenService.setRefreshToken(data.data.refreshToken);
      }

      if (data.data.accessToken) {
        tokenService.setAccessToken(data.data.accessToken);
      }

      processQueue(null, data.data.accessToken);

      if (originalRequest.headers) {
        originalRequest.headers.Authorization = `Bearer ${data.data.accessToken}`;
      }
      return webApi(originalRequest);
    } catch (refreshError) {
      processQueue(refreshError, null);
      forceLogout();
      return Promise.reject(refreshError);
    } finally {
      isRefreshing = false;
    }
  },
);
