import "server-only";
import { headers } from "next/headers";
import { env } from "@/env";
import { ApiError, parseProblem } from "@/lib/api-error";
import { auth } from "@/lib/auth";

/** Délai d'**établissement** d'une réponse relayée en flux — désarmé dès les en-têtes reçus. */
const STREAM_CONNECT_TIMEOUT_MS = 30_000;

async function getJwt(): Promise<string> {
  const result = await auth.api.getToken({ headers: await headers() });
  if (!result?.token) throw new ApiError(401, "Unauthorized", "Session absente");
  return result.token;
}

async function apiRequest(path: string, init: RequestInit = {}): Promise<Response> {
  const token = await getJwt();
  // `new Headers(init.headers)` couvre les trois formes de `HeadersInit` ; un spread d'objet
  // perdrait silencieusement les en-têtes d'un `Headers` ou d'un tableau de paires.
  const requestHeaders = new Headers(init.headers);
  requestHeaders.set("Authorization", `Bearer ${token}`);
  const res = await fetch(`${env.API_URL}${path}`, {
    ...init,
    headers: requestHeaders,
    cache: "no-store",
    signal: init.signal ?? AbortSignal.timeout(10_000),
  });
  if (!res.ok) throw await parseProblem(res);
  return res;
}

export async function apiFetch<T>(path: string, init: RequestInit = {}): Promise<T> {
  const requestHeaders = new Headers(init.headers);
  requestHeaders.set("Accept", "application/json");
  const res = await apiRequest(path, { ...init, headers: requestHeaders });
  // DELETE → 204 sans corps : res.json() lèverait une SyntaxError.
  if (res.status === 204) return undefined as T;
  return (await res.json()) as T;
}

/**
 * Appel authentifié dont **le corps est relayé en flux**, sans être lu ici — le
 * téléchargement d'`errors.csv` par son route handler, seul cas à ce jour.
 *
 * Le délai ne couvre que l'établissement de la réponse et il est **désarmé dès les en-têtes
 * reçus**. `AbortSignal.timeout` d'`apiRequest` ferait l'inverse : il court jusqu'à la fin de
 * la lecture du corps, or ce corps est consommé au rythme du navigateur (backpressure). Un
 * `errors.csv` de plusieurs Mio téléchargé sur une connexion lente dépasserait les 10 s et le
 * flux serait avorté en plein vol — **fichier tronqué, sans la moindre erreur visible**.
 *
 * Ce qui reste non couvert, et ne peut pas l'être ici : une rupture de flux *après* les
 * en-têtes. Le navigateur voit alors un téléchargement interrompu (la réponse n'a pas de
 * `Content-Length`, elle est en chunked), ce qui est le comportement HTTP correct — il n'y a
 * pas de statut à réécrire une fois les en-têtes partis.
 */
export async function apiStream(path: string): Promise<Response> {
  const token = await getJwt();
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), STREAM_CONNECT_TIMEOUT_MS);
  try {
    const res = await fetch(`${env.API_URL}${path}`, {
      headers: { Authorization: `Bearer ${token}` },
      cache: "no-store",
      signal: controller.signal,
    });
    if (!res.ok) throw await parseProblem(res);
    return res;
  } finally {
    // Les en-têtes sont là (ou l'appel a échoué) : le compte à rebours n'a plus rien à
    // garder, et surtout il ne doit pas survivre pour avorter la lecture du corps.
    clearTimeout(timer);
  }
}
