import axios from "axios";
import { message as antMessage } from "antd";

// 请求缓存：短时间内相同请求复用结果
interface CacheEntry {
  data: unknown;
  timestamp: number;
}

const requestCache = new Map<string, CacheEntry>();
const CACHE_TTL = 5000; // 5 秒缓存

function getCacheKey(config: {
  url?: string;
  params?: unknown;
  method?: string;
}): string {
  return `${config.method || "get"}:${config.url || ""}:${JSON.stringify(config.params || {})}`;
}

const defaultAdapter = axios.getAdapter(axios.defaults.adapter);

const client = axios.create({
  baseURL: "/api/v1",
  timeout: 10000,
  headers: { "Content-Type": "application/json" },
  adapter: (config) => {
    // GET 请求检查缓存
    if (config.method === "get" && config.url) {
      const key = getCacheKey(config);
      const entry = requestCache.get(key);
      if (entry && Date.now() - entry.timestamp < CACHE_TTL) {
        return Promise.resolve({
          data: entry.data,
          status: 200,
          statusText: "OK (cached)",
          headers: new axios.AxiosHeaders(),
          config,
        });
      }
    }
    return defaultAdapter(config);
  },
});

// Decode JWT payload without external library
function decodeJwtPayload(
  token: string,
): { exp?: number; sub?: string } | null {
  try {
    const parts = token.split(".");
    if (parts.length !== 3) return null;
    const payload = JSON.parse(
      atob(parts[1].replace(/-/g, "+").replace(/_/g, "/")),
    );
    return payload;
  } catch {
    return null;
  }
}

// Check if token is expired or about to expire (within 30s buffer)
function isTokenExpiringSoon(token: string): boolean {
  const payload = decodeJwtPayload(token);
  if (!payload?.exp) return true;
  return Date.now() / 1000 > payload.exp - 30;
}

// Request interceptor: attach access token + proactive refresh
client.interceptors.request.use(async (config) => {
  const token = localStorage.getItem("access_token");
  if (token) {
    // If token is about to expire, proactively refresh before sending the request
    if (isTokenExpiringSoon(token)) {
      const refreshToken = localStorage.getItem("refresh_token");
      if (refreshToken && !isRefreshing) {
        isRefreshing = true;
        try {
          const { data } = await axios.post("/api/v1/auth/refresh", {
            refresh_token: refreshToken,
          });
          const newAccess = data.data.access_token;
          const newRefresh = data.data.refresh_token;

          // 验证新 token 的用户身份一致
          const currentUserSub = decodeJwtPayload(token)?.sub;
          const newTokenSub = decodeJwtPayload(newAccess)?.sub;
          if (currentUserSub && newTokenSub && currentUserSub !== newTokenSub) {
            localStorage.removeItem("access_token");
            localStorage.removeItem("refresh_token");
            localStorage.removeItem("user");
            window.location.hash = "/login";
            return Promise.reject(new Error("身份验证失败，请重新登录"));
          }

          localStorage.setItem("access_token", newAccess);
          localStorage.setItem("refresh_token", newRefresh);
          processQueue(null, newAccess);
          config.headers.Authorization = `Bearer ${newAccess}`;
          return config;
        } catch {
          processQueue(new Error("refresh failed"), null);
          // Continue with old token, let 401 handler deal with it
        } finally {
          isRefreshing = false;
        }
      }
    }
    config.headers.Authorization = `Bearer ${token}`;
  }

  return config;
});

// 响应拦截器：缓存 GET 响应 + 自动刷新 token
client.interceptors.response.use(
  (response) => {
    // 缓存 GET 响应
    if (response.config.method === "get" && response.config.url) {
      const key = getCacheKey(response.config);
      requestCache.set(key, { data: response.data, timestamp: Date.now() });
    }
    return response;
  },
  async (error) => {
    const originalRequest = error.config;
    if (
      error.response?.status === 401 &&
      !originalRequest._retry &&
      !originalRequest.url?.includes("/auth/login")
    ) {
      if (isRefreshing) {
        return new Promise((resolve, reject) => {
          failedQueue.push({ resolve, reject });
        }).then((token) => {
          originalRequest.headers.Authorization = `Bearer ${token}`;
          return client(originalRequest);
        });
      }

      originalRequest._retry = true;
      isRefreshing = true;

      try {
        const refreshToken = localStorage.getItem("refresh_token");
        if (!refreshToken) throw new Error("No refresh token");

        const { data } = await axios.post("/api/v1/auth/refresh", {
          refresh_token: refreshToken,
        });

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

        // 验证新 token 的用户身份与当前用户一致，防止并发场景下身份切换
        const currentToken = localStorage.getItem("access_token");
        const currentUserSub = currentToken
          ? decodeJwtPayload(currentToken)?.sub
          : null;
        const newTokenSub = decodeJwtPayload(newAccessToken)?.sub;
        if (currentUserSub && newTokenSub && currentUserSub !== newTokenSub) {
          // 身份不一致，强制登出
          localStorage.removeItem("access_token");
          localStorage.removeItem("refresh_token");
          localStorage.removeItem("user");
          window.location.hash = "/login";
          return Promise.reject(new Error("身份验证失败，请重新登录"));
        }

        localStorage.setItem("access_token", newAccessToken);
        localStorage.setItem("refresh_token", newRefreshToken);

        processQueue(null, newAccessToken);

        originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
        return client(originalRequest);
      } catch (refreshError) {
        processQueue(refreshError, null);
        localStorage.removeItem("access_token");
        localStorage.removeItem("refresh_token");
        window.location.hash = "/login";
        return Promise.reject(refreshError);
      } finally {
        isRefreshing = false;
      }
    }

    return Promise.reject(error);
  },
);

// 全局错误提示（仅对未被组件处理的错误显示）
let globalErrorTimer: ReturnType<typeof setTimeout> | null = null;
function showGlobalError(msg: string) {
  // 防止短时间内弹出大量相同提示
  if (globalErrorTimer) return;
  antMessage.error(msg, 3);
  globalErrorTimer = setTimeout(() => {
    globalErrorTimer = null;
  }, 3000);
}

// 全局错误拦截 — 在响应拦截器之后、组件 catch 之前执行
// 组件可通过 axios config 中设置 skipGlobalError: true 来抑制全局提示
declare module "axios" {
  interface AxiosRequestConfig {
    skipGlobalError?: boolean;
  }
  interface InternalAxiosRequestConfig {
    skipGlobalError?: boolean;
  }
}

client.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.config?.skipGlobalError) {
      return Promise.reject(error);
    }
    if (!error.response) {
      showGlobalError("网络连接异常，请检查网络");
    } else if (error.response.status === 403) {
      // 403 通常是权限不足，不全局提示 — 组件层通过 AuthButton 已隐藏操作入口
    } else if (error.response.status === 404) {
      // 404 通常由组件自行处理（如跳转），不全局提示
    } else if (error.response.status >= 500) {
      showGlobalError("服务器异常，请稍后重试");
    }
    return Promise.reject(error);
  },
);

let isRefreshing = false;
let failedQueue: Array<{
  resolve: (token: string) => void;
  reject: (error: unknown) => void;
}> = [];

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

// 清除缓存（登录/登出时调用）
export function clearApiCache() {
  requestCache.clear();
}

// 通用错误处理：提取后端错误消息并展示
export function handleApiError(err: unknown, fallback = "操作失败"): string {
  const msg =
    (err as { response?: { data?: { message?: string } } })?.response?.data
      ?.message || fallback;
  antMessage.error(msg);
  return msg;
}

export default client;
