// ═══════════════════════════════════════════════════
//  lib/api/publicFetch.ts
//
//  A no-auth sibling of fetchApi, for PUBLIC routes a guest opens with no
//  account (the invite link). Unlike fetchApi it:
//    • does NOT attach an Authorization token
//    • does NOT redirect to /login on 401
//  so an expired/invalid invite shows a clean message instead of bouncing
//  a guest to your login page.
// ═══════════════════════════════════════════════════

export { API_BASE_URL } from './http';

export async function publicFetch<T>(
  url: string,
  options?: RequestInit,
): Promise<T> {
  let res: Response;
  try {
    res = await fetch(url, {
      ...options,
      headers: {
        'Content-Type': 'application/json',
        ...(options?.headers || {}),
      },
    });
  } catch {
    throw new Error(
      'Unable to reach the meeting server. Please check your connection and try again.',
    );
  }

  if (res.status === 204) return undefined as unknown as T;

  if (!res.ok) {
    const text = await res.text();
    let message = text;
    try {
      const json = JSON.parse(text);
      message = Array.isArray(json.message)
        ? json.message.join(', ')
        : json.message || text;
    } catch {
      /* keep raw text */
    }
    const err = new Error(message || 'Request failed') as Error & {
      status?: number;
    };
    err.status = res.status;
    throw err;
  }

  const contentType = res.headers.get('content-type') || '';
  if (!contentType.includes('application/json')) {
    return (await res.text()) as unknown as T;
  }
  const text = await res.text();
  if (!text) return undefined as unknown as T;
  return JSON.parse(text) as T;
}