/**
 * Server-side API access (design D8, D12).
 *
 * Only server components call these. A token must never appear in a request the browser
 * makes, so `/i/{token}` is fetched here and only a mapped view model crosses into client
 * components.
 *
 * Every type below is derived from `schema.d.ts`, which is generated from the API's
 * OpenAPI document. There are no hand-written request or response types in the frontend:
 * rename a field in Pydantic and this file stops compiling, which is the point.
 * Regenerate with `make client`.
 */
import "server-only";

import type { components } from "./schema";

const API_BASE = process.env.INTERNAL_API_URL ?? "http://api:8000";

export type ApiEvent = components["schemas"]["EventRead"];
export type ApiCouple = components["schemas"]["CoupleRead"];
export type ApiGuestPrefill = components["schemas"]["GuestPrefill"];
export type ApiCurrentRsvp = components["schemas"]["CurrentRsvpRead"];
export type ApiTokenInvitation = components["schemas"]["TokenInvitationRead"];
export type ApiOpenEvent = components["schemas"]["OpenEventRead"];
export type ApiUnsubscribe = components["schemas"]["UnsubscribeRead"];
export type ApiCard = components["schemas"]["CardRead"];

/** Returns null for 404 so callers can render notFound() rather than an error page. */
async function getJson<T>(path: string, revalidate: number | false): Promise<T | null> {
  const response = await fetch(`${API_BASE}${path}`, {
    headers: { accept: "application/json" },
    ...(revalidate === false
      ? { cache: "no-store" as const }
      : { next: { revalidate } }),
  });

  if (response.status === 404) return null;
  if (!response.ok) {
    throw new Error(`API ${path} failed: ${response.status}`);
  }
  return (await response.json()) as T;
}

/**
 * Personalised and token-bearing: never cached, at any layer.
 *
 * `asPreview` asks the API not to record the open (design D9). It is set when the fetch is
 * a link-preview crawler rather than a guest — otherwise an admin pasting an invitation
 * into WhatsApp would have the platform's crawler mark that guest's invitation opened
 * before the guest had seen it. The payload returned is identical either way.
 */
export function fetchInvitationByToken(token: string, asPreview = false) {
  const path = `/api/invitations/by-token/${encodeURIComponent(token)}`;
  return getJson<ApiTokenInvitation>(asPreview ? `${path}?preview=1` : path, false);
}

/** Opt-out preview. Token-bearing, so it is fetched here and never cached. */
export function fetchUnsubscribe(token: string) {
  return getJson<ApiUnsubscribe>(
    `/api/unsubscribe/${encodeURIComponent(token)}`,
    false,
  );
}

/** Public and identical for everyone, so it may be revalidated on an interval (ISR). */
export function fetchOpenEvent(slug: string) {
  return getJson<ApiOpenEvent>(`/api/events/${encodeURIComponent(slug)}`, 60);
}
