import app from "@/constants/app";
import { IHttpResponse, IObject } from "@/types/interface";
import router from "@/router";
import axios, { AxiosRequestConfig } from "axios";
import qs from "qs";
import { getAccessToken, getRefreshToken } from "./cache";
import { getValueByKeys } from "./utils";
import { ElMessage } from "element-plus";
import { useAppStore } from "@/stores";

/**
 * Create an axios instance with default config
 */
const http = axios.create({
  // baseURL can be enabled if needed, e.g. baseURL: app.api,
  timeout: app.requestTimeout
});

/**
 * Request interceptor to set headers, attach tokens, and handle request data format
 */
http.interceptors.request.use(
  (config: any) => {
    // Add default headers
    config.headers = config.headers || {};
    config.headers["X-Requested-With"] = "XMLHttpRequest";
    config.headers["Request-Start"] = new Date().getTime();

    const store = useAppStore();

    // Add device fingerprint header
    if (store.visitorId) {
      config.headers["X-Device-Fingerprint"] = store.visitorId;
    }

    // Attach Authorization token if present
    const accessToken = getAccessToken();
    if (accessToken) {
      config.headers["Authorization"] = `Bearer ${accessToken}`;
    }

    // Add timestamp to GET and DELETE requests to prevent caching
    if (config.method && ["GET", "DELETE"].includes(config.method.toUpperCase())) {
      config.params = { ...(config.params || {}), _t: Date.now() };
    }

    // For logout or refresh API, use refresh token instead
    if (config.url === "/agent/auth/logout" || config.url === "/agent/auth/refresh") {
      const refreshToken = getRefreshToken();
      if (refreshToken) {
        config.headers["Authorization"] = `Bearer ${refreshToken}`;
      } else {
        // If no refresh token, clear authorization header to prevent sending a stale access token
        delete config.headers["Authorization"];
      }
    }

    // Handle 'application/x-www-form-urlencoded' content type: stringify data
    // Axios usually handles 'application/json' automatically when config.data is an object.
    const contentType = config.headers["Content-Type"] || config.headers["content-type"];
    if (
      contentType === "application/x-www-form-urlencoded" &&
      config.data &&
      typeof config.data === "object" &&
      !(config.data instanceof FormData) // Exclude FormData from qs.stringify
    ) {
      config.data = qs.stringify(config.data);
    }

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

// --- Response Interceptor ---

/**
 * Intercepts incoming responses to handle common HTTP errors, show user-friendly messages,
 * and redirect on unauthorized access.
 */
http.interceptors.response.use(
  (response) => response,
  (error) => {
    // Get HTTP status code from response, default to 500 if not available (e.g., network error)
    const status = getValueByKeys(error, "response.status", 500);

    // Map common HTTP status codes to user-friendly messages
    const httpCodeLabel: IObject<string> = {
      400: "请求参数错误",
      401: "未授权，请登录",
      403: "拒绝访问",
      404: `请求地址出错: ${getValueByKeys(error, "response.config.url", "")}`,
      408: "请求超时",
      500: "API接口报500错误",
      501: "服务未实现",
      502: "网关错误",
      503: "服务不可用",
      504: "网关超时",
      505: "HTTP版本不受支持"
    };

    // Log error ID from backend response for traceability (if backend provides one)
    if (error?.response?.data?.id) {
      console.error("错误信息ID:", error.response.data.id);
    }

    // Determine the message to display to the user:
    // 1. Prefer message from backend response (e.g., error.response.data.message).
    // 2. Fallback to mapped HTTP status code message.
    // 3. Fallback to a generic "Request failed." message.
    const msg =
      (error?.response?.data as IObject)?.message || // Attempt to get message from response data
      httpCodeLabel[status] ||
      "Request failed."; // 请求失败

    // Display the error message to the user using Element Plus
    ElMessage.error(msg);

    // Redirect to login on unauthorized errors
    if (status === 401) {
      redirectLogin();
    }

    // Reject the Promise with the generated error message.
    // Note: For more advanced error handling downstream, you might prefer to reject
    // with the original AxiosError object, or a custom error class wrapping it,
    // to preserve more context (e.g., error.response, error.config).
    return Promise.reject(new Error(msg));
  }
);

/**
 * Redirect to login page
 */
const redirectLogin = () => {
  router.replace("/login");
};

/**
 * Wrapper function to perform HTTP requests with axios instance
 * @param o - Axios request configuration
 * @returns Promise resolving with IHttpResponse data
 */
export default async (o: AxiosRequestConfig): Promise<IHttpResponse> => {
  const res = await http(o);
  return res.data;
};
Here is http helper ts file. I want to add a new feature. When request an api,
 the access token is expired. Please keep this request, use refresh token to apply
 a new token(include accessToken and refreshToken). After that, call previous request
 again. Please help me to fixt this. Please pay attention to the concurrency of the program