// ============================================================
// Axios 请求封装
//
// 功能：
//   - 统一 BaseURL（环境变量 VITE_API_BASE_URL）
//   - 请求拦截器：自动附加 Token
//   - 响应拦截器：统一错误处理、业务码校验
//   - 类型安全的 GET / POST / PUT / DELETE / UPLOAD
//   - 请求超时、取消请求支持
// ============================================================

import axios, {
   type AxiosInstance,
   type AxiosRequestConfig,
   type AxiosResponse,
   type InternalAxiosRequestConfig,
} from 'axios';
import type { ApiResponse } from './types';
import { RequestError, RequestErrorType, RefreshError, isRefreshError } from './types';
import { emit, EVENTS } from '../utils/bus';
import { logger } from '../utils/bus';
import { getSafeStore, setSafeStore, clearSafeStore, initSafeStore } from '../utils/safe-storage';

// ---------- 配置 ----------

/** API 基础地址（可通过 Vite 环境变量覆盖） */
const BASE_URL = import.meta.env.VITE_API_BASE_URL;
/** 请求超时时间（毫秒） */
const TIMEOUT = 30_000;

/** localStorage 中 Token 的键名（密文由 safeStorage 保护） */
const TOKEN_KEY = 'ele-heixiu-token';

/** refresh token 接口路径（该接口 401 时不应触发 handle401，否则死循环） */
const REFRESH_TOKEN_URL = '/api/v1/auth/refresh';

/** 判断请求是否为 refresh token 接口 */
function isRefreshRequest(config: InternalAxiosRequestConfig): boolean {
   return config.url?.includes(REFRESH_TOKEN_URL) ?? false;
}

// ---------- Token 管理 ----------

/**
 * 启动时初始化 Token：从加密存储解密到内存缓存。
 * 应在 AppProvider 挂载时调用（login/logout 之前）。
 */
export async function initTokenStore(): Promise<void> {
   await initSafeStore(TOKEN_KEY);
}

/** 获取当前内存缓存中的 Token（同步，高频安全） */
export function getToken(): string | null {
   return getSafeStore(TOKEN_KEY);
}

/** 设置 Token（异步加密存储 + 更新内存缓存） */
export async function setToken(token: string): Promise<void> {
   await setSafeStore(TOKEN_KEY, token);
}

/** 清除 Token（同步清除内存缓存 + localStorage） */
export function clearToken(): void {
   clearSafeStore(TOKEN_KEY);
}

// ---------- Token 刷新（由外部注册，request.ts 不依赖任何 auth 模块） ----------

let refreshHandler: (() => Promise<string | null>) | null = null;
let isRefreshing = false;
let refreshPromise: Promise<string | null> | null = null;
/** 等待刷新完成的请求队列 */
let failedQueue: Array<{
   resolve: (token: string) => void;
   reject: (error: Error) => void;
}> = [];

/** 由 auth 模块调用，注册 token 刷新回调 */
export function setTokenRefreshHandler(handler: () => Promise<string | null>): void {
   refreshHandler = handler;
}

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

// ---------- Axios 实例 ----------

const instance: AxiosInstance = axios.create({
   baseURL: BASE_URL,
   timeout: TIMEOUT,
   headers: {
      'Content-Type': 'application/json',
   },
});

// ---------- 请求拦截器 ----------

instance.interceptors.request.use(
   (config: InternalAxiosRequestConfig) => {
      // 自动附加 Token
      const token = getToken();
      if (token && config.headers) {
         config.headers.Authorization = `Bearer ${token}`;
      }
      return config;
   },
   () => {
      return Promise.reject(new RequestError(RequestErrorType.NETWORK, '请求配置失败'));
   },
);

// ---------- 401 → 刷新 Token + 重试 ----------

function handle401(config: InternalAxiosRequestConfig): Promise<AxiosResponse<ApiResponse>> {
   // 已经在刷新中 → 排队等待
   if (isRefreshing) {
      return new Promise((resolve, reject) => {
         failedQueue.push({
            resolve: (token) => {
               config.headers.Authorization = `Bearer ${token}`;
               resolve(instance(config));
            },
            reject,
         });
      });
   }

   // 没有注册刷新回调 → 直接要求重新登录
   if (!refreshHandler) {
      clearToken();
      emit(EVENTS.AUTH_REQUIRED);
      return Promise.reject(new RefreshError());
   }

   // 开始刷新
   isRefreshing = true;
   refreshPromise = refreshHandler();

   return new Promise((resolve, reject) => {
      refreshPromise!
         .then((newToken) => {
            if (newToken) {
               // 刷新成功 → 用新 token 重试队列和当前请求
               processQueue(null, newToken);
               config.headers.Authorization = `Bearer ${newToken}`;
               resolve(instance(config));
            } else {
               // null = 临时失败（网络超时/无连接）
               // → 保留 token，仅拒绝当前请求；下次 401 再试（不弹登录）
               processQueue(new RefreshError(), null);
               reject(new RefreshError());
            }
            isRefreshing = false;
            refreshPromise = null;
         })
         .catch((err) => {
            // throw = 永久失败（refresh_token 已过期/无效）
            // → 清 token + 弹登录
            logger.warn('auth', 'Token 刷新永久失败', err instanceof Error ? err : undefined);
            processQueue(err, null);
            isRefreshing = false;
            refreshPromise = null;
            clearToken();
            emit(EVENTS.AUTH_REQUIRED);
            reject(new RefreshError());
         });
   });
}

// ---------- 响应拦截器 ----------

instance.interceptors.response.use(
   (response: AxiosResponse<ApiResponse>) => {
      const { data } = response;

      // HTTP 200 但业务码非 0 → 业务错误
      if (data && data.code !== 0) {
         // 业务码 401 → 尝试刷新 token 并重试（refresh 接口自身 401 直接拒绝）
         if (data.code === 401 && response.config) {
            if (isRefreshRequest(response.config)) {
               return Promise.reject(new RefreshError());
            }
            return handle401(response.config);
         }

         // 业务码 1001 → 余额不足（防御性兜底：当前后端实际返回 HTTP 402 + code 1001，
         // 已在上方 HTTP error 拦截器处理；此处保留以兼容「HTTP 200 + code 1001」的变体）
         if (data.code === 1001) {
            console.log('[request] 检测到余额不足 code=1001，发射 BALANCE_INSUFFICIENT 事件');
            emit(EVENTS.BALANCE_INSUFFICIENT, {
               code: 1001,
               message: data.message || '余额不足，请充值后重试',
            });
         }

         logger.warn('request', `Business error: ${data.message || '请求失败'}`, undefined, {
            url: response.config.url,
            method: response.config.method?.toUpperCase(),
            code: data.code,
            traceId: data.traceId,
         });

         const businessErr = new RequestError(RequestErrorType.BUSINESS, data.message || '请求失败', {
            code: data.code,
            traceId: data.traceId,
         });
         // 余额不足已通过事件总线 → Modal 展示，标记避免调用方再弹 message.error toast
         if (data.code === 1001) {
            businessErr.displayed = true;
         }
         return Promise.reject(businessErr);
      }


      return response;
   },
   (error) => {
      // 提取请求上下文（所有日志共用）
      const reqCtx = {
         url: error.config?.url,
         method: error.config?.method?.toUpperCase(),
      };

      // 全局捕获 RefreshError → 自动触发登录 Modal
      if (isRefreshError(error)) {
         logger.debug('request', 'Auth expired (RefreshError caught)', {
            ...reqCtx,
            errorType: error.name,
            errorMessage: error.message,
         });
         clearToken();
         emit(EVENTS.AUTH_REQUIRED);
         return Promise.reject(error);
      }

      // 请求已被取消
      if (axios.isCancel(error)) {
         const cancelErr = new RequestError(RequestErrorType.CANCELLED, '请求已取消');
         logger.debug('request', 'Request cancelled', reqCtx);
         return Promise.reject(cancelErr);
      }

      // 超时
      if (error.code === 'ECONNABORTED') {
         const timeoutErr = new RequestError(RequestErrorType.TIMEOUT, '请求超时，请检查网络');
         logger.warn('request', 'Request timeout', timeoutErr, reqCtx);
         return Promise.reject(timeoutErr);
      }

      // 无网络
      if (!error.response) {
         const netErr = new RequestError(RequestErrorType.NETWORK, '网络异常，请检查连接');
         logger.warn('request', 'Network error', netErr, reqCtx);
         return Promise.reject(netErr);
      }

      // HTTP 状态码错误
      const { status, data } = error.response;

      // 401 → 尝试刷新 token 并重试
      // ⚠️ 同时检查响应体中的业务码 data.code：
      //    data.code === 401  → token 过期 → 走刷新流程
      //    data.code !== 401  → 其他认证错误（如密码错误）→ 透传后端 message
      if (status === 401 && error.config && data?.code === 401) {
         if (isRefreshRequest(error.config)) {
            const refreshErr = new RefreshError();
            logger.debug('request', 'Refresh endpoint returned 401', {
               ...reqCtx,
               errorType: refreshErr.name,
            });
            return Promise.reject(refreshErr);
         }
         return handle401(error.config);
      }

      // 402 + code 1001 → 余额不足（Payment Required），事件总线驱动弹窗引导充值
      if (status === 402 && data?.code === 1001) {
         const balanceMsg = data?.message || '余额不足，请充值后重试';
         console.log('[request] 检测到 HTTP 402 + code 1001 余额不足，发射 BALANCE_INSUFFICIENT 事件');
         emit(EVENTS.BALANCE_INSUFFICIENT, {
            code: 1001,
            message: balanceMsg,
         });
      }

      // 403 + code 4040 → 软件使用权到期，事件总线驱动弹窗引导购买 VIP
      if (status === 403 && data?.code === 4040) {
         const subMsg = data?.message || '软件使用权已到期，请购买 VIP 套餐后继续使用';
         console.log('[request] 检测到 HTTP 403 + code 4040 软件到期，发射 SUBSCRIPTION_EXPIRED 事件');
         emit(EVENTS.SUBSCRIPTION_EXPIRED, {
            code: 4040,
            message: subMsg,
         });
      }

      let errMsg = '请求失败';

      switch (status) {
         case 400:
            errMsg = '请求参数有误';
            break;
         case 402:
            errMsg = '余额不足';
            break;
         case 403:
            errMsg = '没有访问权限';
            break;
         case 404:
            errMsg = '请求的资源不存在';
            break;
         case 500:
            errMsg = '服务器内部错误';
            break;
         case 502:
            errMsg = '网关错误';
            break;
         case 503:
            errMsg = '服务暂时不可用';
            break;
      }

      const httpErr = new RequestError(RequestErrorType.HTTP, data?.message || errMsg, {
         httpStatus: status,
         code: data?.code,
      });

      // 已通过事件总线 Modal 展示的错误，标记避免调用方再弹 message.error toast
      // 双条件精确匹配：402+1001（余额不足）/ 403+4040（软件到期），避免误拦截同状态码的其他业务
      if ((status === 402 && data?.code === 1001) || (status === 403 && data?.code === 4040)) {
         httpErr.displayed = true;
      }

      // HTTP 5xx → error 级别，4xx → warn 级别
      if (status >= 500) {
         logger.error('request', `HTTP ${status}: ${errMsg}`, httpErr, reqCtx);
      } else {
         logger.warn('request', `HTTP ${status}: ${errMsg}`, httpErr, reqCtx);
      }

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

// ---------- 请求方法 ----------

/**
 * 发起 GET 请求
 * @param url    - 请求路径
 * @param params - 查询参数
 * @param config - Axios 配置覆盖
 */
export async function get<T>(
   url: string,
   params?: Record<string, unknown>,
   config?: AxiosRequestConfig,
): Promise<T> {
   const res = await instance.get<ApiResponse<T>>(url, { ...config, params });
   return res.data.data;
}

/**
 * 发起 POST 请求（JSON）
 * @param url  - 请求路径
 * @param data - 请求体
 * @param config - Axios 配置覆盖
 */
export async function post<T>(
   url: string,
   data?: unknown,
   config?: AxiosRequestConfig,
): Promise<T> {
   const res = await instance.post<ApiResponse<T>>(url, data, config);
   return res.data.data;
}

/**
 * 发起 PUT 请求（JSON）
 */
export async function put<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {
   const res = await instance.put<ApiResponse<T>>(url, data, config);
   return res.data.data;
}

/**
 * 发起 DELETE 请求
 */
export async function del<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
   const res = await instance.delete<ApiResponse<T>>(url, config);
   return res.data.data;
}

/**
 * 文件上传（multipart/form-data）
 * @param url      - 上传接口路径
 * @param formData - FormData 对象
 * @param onProgress - 上传进度回调 (0~100)
 */
export async function upload<T>(
   url: string,
   formData: FormData,
   onProgress?: (percent: number) => void,
): Promise<T> {
   const res = await instance.post<ApiResponse<T>>(url, formData, {
      headers: { 'Content-Type': 'multipart/form-data' },
      timeout: 120000, // 上传超时 2 分钟
      onUploadProgress: (progressEvent) => {
         if (onProgress && progressEvent.total) {
            const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
            onProgress(percent);
         }
      },
   });
   return res.data.data;
}

/**
 * 文件下载
 * @param url      - 下载接口路径
 * @param filename - 保存的文件名
 */
export async function download(url: string, filename?: string): Promise<void> {
   const res = await instance.get(url, {
      responseType: 'blob',
   });

   const blob = new Blob([res.data]);
   const downloadUrl = window.URL.createObjectURL(blob);
   const anchor = document.createElement('a');
   anchor.href = downloadUrl;
   anchor.download = filename || 'download';
   document.body.appendChild(anchor);
   anchor.click();
   document.body.removeChild(anchor);
   window.URL.revokeObjectURL(downloadUrl);
}

/**
 * 创建可取消的请求控制器
 * 用法：
 *   const { controller, request } = createCancellableRequest();
 *   request(get('/some/url'));  // 发起请求
 *   controller.abort();         // 取消请求
 */
export function createCancellableRequest() {
   const controller = new AbortController();
   return {
      controller,
      signal: controller.signal,
      /** 包裹一个请求 Promise，使其可通过 controller.abort() 取消 */
      request: <T>(promise: Promise<T>): Promise<T> => {
         return new Promise<T>((resolve, reject) => {
            promise.then(resolve).catch(reject);
            controller.signal.addEventListener('abort', () => {
               reject(new RequestError(RequestErrorType.CANCELLED, '请求已取消'));
            });
         });
      },
   };
}

// ---------- 导出 axios 实例（供高级场景直接使用） ----------
export { instance as axiosInstance };
export { RequestError, RequestErrorType };
