// services/analytics/src/firecrawl-fetch.ts — adapte `scrapeOne` (Task 1,
// ./firecrawl.ts) à la signature `fetchHtml` attendue par le runner sémantique
// (`{ html: string; finalUrl: string } | null`, cf.
// src/lib/semantic/analysis-runner.ts:75 / `fetchSemanticHtml`). Câblé par
// serp-semantics.ts (Task 4) quand `firecrawlConfigured()` est vrai.
import { scrapeOne } from "./firecrawl";

/** Résultat attendu par le runner sémantique — même shape que
 *  `fetchSemanticHtml` (src/lib/semantic/fetch.ts). */
type SemanticFetchResult = { html: string; finalUrl: string };

/**
 * Wrapper trivial `scrapeOne` → shape runner. `scrapeOne` ne throw jamais
 * (contrat Task 1, durci en review — y compris body non-JSON) : ce wrapper
 * ne throw jamais non plus, il traduit `status !== "ok"` (blocked/timeout/
 * empty/fetch_error) — ainsi qu'un `status: "ok"` défensif sans HTML — en
 * `null`, le contrat "page inexploitable" du runner. `scraper` est
 * injectable pour les tests (DI, pas de mock.module).
 *
 * `finalUrl: url` (l'URL DEMANDÉE, `FirecrawlPage.url` = sourceURL) plutôt
 * que l'URL post-redirect : on perd l'URL finale après redirect, acceptable
 * ici — le runner s'en sert pour l'affichage/dédup, pas pour re-fetch.
 */
export async function fetchViaFirecrawl(
  url: string,
  scraper: typeof scrapeOne = scrapeOne,
): Promise<SemanticFetchResult | null> {
  const page = await scraper(url);
  if (page.status !== "ok" || !page.html) {
    return null;
  }
  return { html: page.html, finalUrl: url };
}
