export type ApiError = {
  code: string;
  message: string;
  status: number;
};

export type User = {
  id: string;
  username: string;
  is_admin: boolean;
};

export type LoginResponse = {
  token: string;
  user: User;
};

export async function apiFetch(path: string, init?: RequestInit): Promise<unknown> {
  const res = await fetch(path, {
    credentials: 'same-origin',
    headers: { 'Content-Type': 'application/json', ...(init?.headers ?? {}) },
    ...init
  });
  const body = res.status === 204 ? null : await res.json().catch(() => null);
  if (!res.ok) {
    if (res.status === 401) {
      // Lazy import: auth/store imports from this file, so a top-level
      // import would be circular. By the time any 401 actually happens
      // at runtime, both modules have finished loading.
      const { logout } = await import('$lib/auth/store.svelte');
      await logout({ silent: true });
    }
    // Two error envelope shapes ship in this codebase today:
    //   1) {"error": {"code": "...", "message": "..."}}  — non-admin endpoints
    //   2) {"error": "code_string"}                      — admin endpoints
    // Without handling (2), every admin error toast renders 'unknown'.
    const errorField = body && (body as { error?: unknown }).error;
    let code = 'unknown';
    let message = res.statusText;
    if (typeof errorField === 'string') {
      code = errorField;
    } else if (errorField && typeof errorField === 'object') {
      const env = errorField as { code?: string; message?: string };
      code = env.code ?? 'unknown';
      message = env.message ?? res.statusText;
    }
    const err: ApiError = { code, message, status: res.status };
    throw err;
  }
  return body;
}

export const api = {
  get: <T>(path: string): Promise<T> => apiFetch(path) as Promise<T>,
  post: <T>(path: string, body: unknown): Promise<T> =>
    apiFetch(path, { method: 'POST', body: JSON.stringify(body) }) as Promise<T>,
  put: <T>(path: string, body: unknown): Promise<T> =>
    apiFetch(path, { method: 'PUT', body: JSON.stringify(body) }) as Promise<T>,
  patch: <T>(path: string, body: unknown): Promise<T> =>
    apiFetch(path, { method: 'PATCH', body: JSON.stringify(body) }) as Promise<T>,
  del: <T = null>(path: string): Promise<T> =>
    apiFetch(path, { method: 'DELETE' }) as Promise<T>
};
