// Request wrapper — all calls go through the Express proxy at /syncro-api/
// The Authorization header is injected server-side; never sent from the browser.
// Session cookie is attached automatically by the browser (same-origin, httpOnly).

export async function apiRequest(method, path, body = null, params = {}, { base = '/syncro-api' } = {}) {
  const url = new URL(base + path, window.location.origin);

  for (const [k, v] of Object.entries(params)) {
    if (v !== null && v !== undefined && v !== '') {
      url.searchParams.set(k, v);
    }
  }

  const options = {
    method,
    credentials: 'same-origin',
    headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
  };

  if (body && (method === 'PUT' || method === 'POST')) {
    options.body = JSON.stringify(body);
  }

  let response;
  try {
    response = await fetch(url.toString(), options);
  } catch (err) {
    throw new Error('Network error — check connection. ' + err.message);
  }

  // Session expired — redirect to login only if the 401 came from our own auth
  // wall (code: SESSION_EXPIRED), not from an upstream Syncro API auth error.
  if (response.status === 401) {
    let body = {};
    try { body = await response.clone().json(); } catch { /* ignore */ }
    if (body.code === 'SESSION_EXPIRED') {
      window.location.href = '/login.html?expired=1';
      throw new Error('Session expired.');
    }
    throw new Error('Upstream authorization error (HTTP 401).');
  }

  // Syncro returns 204 No Content on successful PUT
  if (response.status === 204) return {};

  let data;
  try {
    data = await response.json();
  } catch {
    throw new Error(`Non-JSON response (HTTP ${response.status})`);
  }

  if (!response.ok) {
    const msg = data?.error || data?.message || data?.errors?.join?.(', ') || `HTTP ${response.status}`;
    throw new Error(msg);
  }

  return data;
}
