import axios from "axios";
import { store } from "../store";
import { logout } from "../features/auth/authSlice";
import { toastError } from "../toast";

const unauthorizedCode = [401, 422];
const departmentInactiveCode = 1007;
const tokenExpiredCode = 1002;
const deviceId = "WEB_123";
const deviceType = "web";
const subscriptionExpiredCode = 1008;

let isRefreshing = false; // Track if token refresh is in progress
let refreshTokenPromise: Promise<string | null> | null = null; // Store the refresh token promise
let refreshSubscribers: ((token: string) => void)[] = [];

const api = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_URL,
  headers: {
    "Content-Type": "application/json",
  },
});

const logoutUser = (showDepartmentInactiveMessage = false) => {
  store.dispatch(logout());

  // Show department inactive message if needed
  if (showDepartmentInactiveMessage) {
    toastError(
      "Your department is inactive. Please contact the administrator."
    );
  }

  // Redirect to login page if we're in the browser
  if (typeof window !== "undefined") {
    window.location.href = "/login";
  }
};

const onRefreshed = (token: string) => {
  refreshSubscribers.forEach((cb) => cb(token));
  refreshSubscribers = [];
};

const subscribeTokenRefresh = (cb: (token: string) => void) => {
  refreshSubscribers.push(cb);
};

const refreshAccessToken = async (): Promise<string | null> => {
  try {
    const currentState = store.getState();
    const currentToken = currentState?.auth?.refreshToken || "";

    const response = await axios.post(
      `${process.env.NEXT_PUBLIC_API_URL}/auth/refresh-token`,
      {
        device_id: deviceId,
      },
      {
        headers: {
          Authorization: currentToken,
        },
      }
    );

    if (!response?.data?.success) {
      logoutUser();
      return null;
    }

    const newAccessToken = response?.data?.data?.access_token;
    const newRefreshToken = response?.data?.data?.refresh_token;

    // Dispatch the new token to the Redux store while preserving all existing state
    store.dispatch({
      type: "auth/saveUserDetails",
      payload: {
        accessToken: newAccessToken,
        refreshToken: newRefreshToken,
        user: currentState.auth.user,
        permissions: currentState.auth.permissions,
        isAuthenticated: true,
      },
    });

    return newAccessToken;
  } catch (error) {
    logoutUser();
    return null;
  }
};

api.interceptors.request.use(
  (config) => {
    const persistData = store.getState();

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    let accessToken = (persistData as any)?.auth?.accessToken;

    if (!accessToken) {
      const { auth } = store.getState();
      accessToken = auth.accessToken;
      config.headers.device_id = deviceId;
      config.headers.device_type = deviceType;
    }

    if (accessToken) {
      config.headers.Authorization = accessToken;
      config.headers.device_id = deviceId;
      config.headers.device_type = deviceType;
    } else {
      console.warn(
        "API Request without token:",
        config.method?.toUpperCase(),
        config.url
      );
    }

    // Handle FormData - remove Content-Type header so axios can set it with boundary
    if (config.data instanceof FormData) {
      delete config.headers["Content-Type"];
    }

    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

api.interceptors.response.use(
  (response) => response,
  async (error) => {
    const { response } = error;

    const code = response?.data?.code ?? null;

    // Handel Subscription Expired error - logout with message
    if (code === subscriptionExpiredCode) {
      logoutUser(true);
      return Promise.reject(error);
    }

    // Handle DEPARTMENT_INACTIVE error - logout with message
    if (code === departmentInactiveCode) {
      logoutUser(true);
      return Promise.reject(error);
    }

    // Handle TOKEN_EXPIRED - try to refresh token
    if (code === tokenExpiredCode) {
      const originalRequest = error.config;

      if (!isRefreshing) {
        isRefreshing = true;
        refreshTokenPromise = refreshAccessToken();

        refreshTokenPromise.then((newToken) => {
          isRefreshing = false;
          refreshTokenPromise = null;
          if (newToken) onRefreshed(newToken);
        });
      }

      const token = await new Promise<string | null>((resolve) => {
        subscribeTokenRefresh((newToken) => {
          resolve(newToken);
        });
      });

      if (token) {
        originalRequest.headers["Authorization"] = token;
        return axios(originalRequest);
      } else {
        // If refresh token failed, logout user
        logoutUser();
        return Promise.reject(error);
      }
    }

    // Handle UNAUTHORIZED - logout immediately
    // Note: 422 is a validation error, not an authentication error, so we don't logout on it
    if (
      response?.data?.success === false &&
      unauthorizedCode.includes(response?.status || code) &&
      response.status === 401
    ) {
      logoutUser();
      return Promise.reject(error);
    }

    return Promise.reject(error);
  }
);

export default api;
