interface RequestOptions extends RequestInit {
  timeout?: number;
  // 忽略存储
  ignoreStorage?: boolean;
  // 解析返回结果
  parseResult?: boolean;
}

// 请求返回数据格式
interface ResponseData<T = any> {
  code: number;
  isSuccess: boolean;
  result: T;
  error?: string;
  errors?: {
    message: string;
  };
}

class RequestError extends Error {
  constructor(
    public status: number,
    message: string,
  ) {
    super(message);
    this.name = 'RequestError';
  }
}

const DEFAULT_TIMEOUT = 1000 * 60 * 30;
const BASE_URL = process.env.NODE_ENV === 'production' ? import.meta.env.VITE_APP_SERVER : '/api';

export async function request<T = any>(url: string, options: RequestOptions = {}): Promise<T> {
  const { timeout = DEFAULT_TIMEOUT, parseResult = true, ignoreStorage, ...fetchOptions } = options;

  // 增加默认headers
  const headers = new Headers(fetchOptions.headers);
  if (!headers.has('Content-Type')) {
    headers.set('Content-Type', 'application/json');
  }

  // 增加token
  const token = (localStorage.getItem('oauth2token') || "")?.replace("\"", '')?.replace("\"", '');
  if (token) {
    headers.set('authorization', token);
  }

  // 创建超时终止
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeout);
  // Storage key 根据请求方法、url、参数生成key 获取到数据更新到本地
  // 数据接口返回异常则使用本地存储数据 (数据异常或数据空)
  let key = `${fetchOptions.method}_${url}`;
  if (fetchOptions.body) {
    key += `_${JSON.stringify(fetchOptions.body)}`;
  }

  try {
    const response = await fetch(BASE_URL + url, {
      ...fetchOptions,
      headers,
      signal: controller.signal,
    });

    clearTimeout(timeoutId);

    if (!response.ok) {
      throw new RequestError(response.status, response.statusText);
    }

    const data: ResponseData<T> = await response.json();

    // 401处理
    if (data.code === 401) {
      // localStorage.removeItem('token');
      // window.location.href = '/#/login';
      throw new Error('Unauthorized');
    }

    // 返回解析的数据
    if (!parseResult) {
      return data as unknown as T;
    }

    if (data.code !== 1) {
      throw new Error(data.errors?.message);
    }

    return data?.result;
  } catch (error) {
    if (error instanceof DOMException && error.name === 'AbortError') {
      throw new Error(`Request timeout after ${timeout}ms`);
    }
    throw error;
  }
}

const buildUrl = (url: string, data?: any) => {
  if (!data) return url;
  return `${url}?${new URLSearchParams(data as Record<string, string>).toString()}`;
};

// Helper methods
export const get = <T>(url: string, data?: any, options?: RequestOptions) => {
  const newUrl = buildUrl(url, data);
  return request<T>(newUrl, { ...options, method: 'GET' });
};

export const post = <T>(url: string, data?: any, options?: RequestOptions) =>
  request<T>(url, {
    ...options,
    method: 'POST',
    body: JSON.stringify(data),
  });

export const put = <T>(url: string, data?: any, options?: RequestOptions) =>
  request<T>(url, {
    ...options,
    method: 'PUT',
    body: JSON.stringify(data),
  });

export const del = <T>(url: string, options?: RequestOptions) =>
  request<T>(url, { ...options, method: 'DELETE' });
