

// lib/upstream.ts
// Helper pour proxy côté serveur vers l'API upstream (API Gateway/Lambda).
// Objectif : être résilient aux variantes de chemin (avec /api ou sans),
// centraliser l'URL de base et uniformiser les appels.
//
// Variables d'environnement attendues (côté SERVEUR, non NEXT_PUBLIC) :
// - UPSTREAM_API_BASE   ex: https://xxxx.execute-api.eu-west-3.amazonaws.com/default
// - UPSTREAM_API_PREFIX ex: "" (par défaut) ou "/api" pour préférer une des variantes
//
// Option debug : DEBUG_UPSTREAM=1 (en dev) pour logger les URLs tentées.

const BASE = process.env.UPSTREAM_API_BASE ?? "";
const PREFERRED_PREFIX = process.env.UPSTREAM_API_PREFIX ?? "";
const DEBUG_UPSTREAM =
  process.env.NODE_ENV !== "production" && process.env.DEBUG_UPSTREAM === "1";

export type UpstreamFetchOpts = {
  method?: string;
  headers?: HeadersInit;
  // Querystring à forwarder (ex: { year: "2025", page: "1" })
  searchParams?: URLSearchParams | Record<string, string | number | undefined>;
  // Corps brut (JSON stringifié par l'appelant si nécessaire)
  body?: BodyInit | null;
};

/**
 * Assure un leading slash unique ("/x" plutôt que "x" ou "//x")
 */
function withLeadingSlash(path: string): string {
  if (!path) return "/";
  return path.startsWith("/") ? path.replace(/\/{2,}/g, "/") : `/${path}`;
}

/**
 * Construit une URL absolue à partir d'une base et d'un chemin relatif.
 * - Normalise les "/" doublons
 * - Ajoute les query params si fournis
 */
function buildUrl(
  base: string,
  path: string,
  query?: UpstreamFetchOpts["searchParams"]
): string {
  const baseNormalized = base.endsWith("/") ? base : base + "/";
  // On enlève le premier "/" pour laisser URL gérer correctement la concaténation
  const relative = withLeadingSlash(path).replace(/^\//, "");
  const url = new URL(relative, baseNormalized);

  if (query) {
    const usp =
      query instanceof URLSearchParams
        ? query
        : new URLSearchParams(
            Object.entries(query).flatMap(([k, v]) =>
              v === undefined ? [] : [[k, String(v)]]
            )
          );
    usp.forEach((v, k) => url.searchParams.set(k, v));
  }

  return url.toString();
}

/**
 * fetchUpstream: tente d'abord la variante préférée (UPSTREAM_API_PREFIX),
 * puis retombe sur l'autre (/api ↔︎ "") si 404.
 * Pour toute autre erreur (401, 403, 500...), on remonte la réponse immédiatement.
 */
export async function fetchUpstream(
  path: string,
  opts: UpstreamFetchOpts = {}
): Promise<Response> {
  if (!BASE) {
    throw new Error(
      "UPSTREAM_API_BASE manquant. Définis la variable d'environnement côté serveur."
    );
  }

  // Ordre de préférence pour les préfixes
  const prefixes =
    PREFERRED_PREFIX === "/api"
      ? ["/api", ""]
      : PREFERRED_PREFIX === ""
      ? ["", "/api"]
      : [PREFERRED_PREFIX, PREFERRED_PREFIX === "/api" ? "" : "/api"];

  const attempts = prefixes.map((p) =>
    p ? withLeadingSlash(p) + withLeadingSlash(path) : withLeadingSlash(path)
  );

  let last404: Response | undefined;

  for (const candidate of attempts) {
    const url = buildUrl(BASE, candidate, opts.searchParams);

    if (DEBUG_UPSTREAM) {
      // eslint-disable-next-line no-console
      console.log("UPSTREAM TRY:", url);
    }

    const resp = await fetch(url, {
      method: opts.method ?? "GET",
      headers: opts.headers,
      body: opts.body ?? null,
      cache: "no-store",
      // Important quand l'upstream est externe : ne jamais envoyer automatiquement des cookies serveur
      // (l'upstream ne les comprendrait pas). On ne met pas credentials ici.
    });

    if (resp.ok) return resp;

    if (resp.status === 404) {
      last404 = resp;
      continue; // essaie l'autre variante
    }

    // Pour les autres codes, on renvoie directement
    return resp;
  }

  // Toutes les tentatives ont renvoyé 404
  if (last404) return last404;

  // Sécurité : ne devrait pas arriver
  return new Response("Not Found", { status: 404 });
}

/**
 * Helper pratique : récupère le JSON directement, avec message d'erreur enrichi.
 * Laisse remonter les codes HTTP. À utiliser dans les route handlers Next.
 */
export async function upstreamJson<T = unknown>(
  path: string,
  opts: UpstreamFetchOpts = {}
): Promise<{ data: T; response: Response }> {
  const resp = await fetchUpstream(path, opts);
  const text = await resp.text();

  if (!resp.ok) {
    // On renvoie quand même le body texte pour faciliter le debug côté route
    throw new Error(
      `Upstream error ${resp.status} on ${path}: ${text.slice(0, 500)}`
    );
  }

  try {
    const data = text ? (JSON.parse(text) as T) : ({} as T);
    return { data, response: resp };
  } catch {
    throw new Error(
      `Upstream JSON parse error on ${path}: ${text.slice(0, 500)}`
    );
  }
}