const pendingReads = new Map();

async function request(path, options) {
  const binaryBody = options.body instanceof ArrayBuffer || options.body instanceof Blob || options.body instanceof FormData;
  const response = await fetch(path, {
    credentials: 'same-origin',
    headers: { ...(options.body && !binaryBody ? { 'Content-Type': 'application/json' } : {}), ...options.headers },
    ...options,
    body: options.body && typeof options.body !== 'string' && !binaryBody ? JSON.stringify(options.body) : options.body
  });
  const type = response.headers.get('content-type') || '';
  const data = type.includes('application/json') ? await response.json() : await response.text();
  if (!response.ok) {
    const error = new Error(data?.message || '操作未完成，请稍后重试');
    error.status = response.status;
    throw error;
  }
  return data;
}

export function api(path, options = {}) {
  const method = String(options.method || 'GET').toUpperCase();
  if (method !== 'GET' || options.body != null || options.signal) return request(path, options);

  // A quick double click or repeated render must not download and parse the
  // same large JSON response more than once while the first request is active.
  const key = String(path);
  if (pendingReads.has(key)) return pendingReads.get(key);
  const loading = request(path, options).finally(() => {
    if (pendingReads.get(key) === loading) pendingReads.delete(key);
  });
  pendingReads.set(key, loading);
  return loading;
}
