// Adaptateurs origine — famille "industrie" : hub /industrie + pages
// Logistique et Distribution (composants standalone). Une entrée par section,
// keyée par le NOM DE FICHIER du composant original. Chaque adaptateur reçoit
// la section WordPress (champs camelCase GraphQL, cf. src/lib/wp/types.ts) et
// rend le composant ORIGINAL en mappant les champs. Valeur WP absente/null =>
// prop undefined (le défaut du composant prend le relais, rendu identique au
// site pré-CMS).
//
// Tertiaire / Collectivités / Agricole / ERP passent par SectorPageTemplate :
// voir src/lib/wp/toSectorConfig.ts (mapper inverse de config, pas d'adaptateur
// section par section).
import { Fragment, type ComponentType, type ReactNode } from "react";
import { sanitizeWpHtml } from "@/lib/sanitizeHtml";
import { arr, mapImage, parseAccent } from "@/lib/wp/mappers";
import type {
  ChiffresBlock,
  ContenuSeoBlock,
  CtaBlock,
  EtapesBlock,
  FaqBlock,
  GrilleCardsBlock,
  HeroBlock,
  TableauComparatifBlock,
  TexteImageBlock,
} from "@/lib/wp/types";

// --- Hub /industrie ---------------------------------------------------------
import IndustrieHero, { defaultProofs as hubProofs } from "@/components/industrie/IndustrieHero";
import IndustrieProblem, {
  defaultFrictions as hubFrictions,
} from "@/components/industrie/IndustrieProblem";
import IndustriePrinciple, {
  defaultBenefits as hubPrincipleBenefits,
} from "@/components/industrie/IndustriePrinciple";
import IndustrieBenefits, {
  defaultBenefits as hubBenefitCards,
} from "@/components/industrie/IndustrieBenefits";
import IndustrieApplications, {
  defaultApplications as hubApplications,
} from "@/components/industrie/IndustrieApplications";
import IndustrieMethod, {
  defaultSteps as hubMethodSteps,
  defaultReassurances as hubMethodReassurances,
} from "@/components/industrie/IndustrieMethod";
import IndustrieProof, {
  defaultFigures as hubProofFigures,
} from "@/components/industrie/IndustrieProof";
import IndustrieFAQ from "@/components/industrie/IndustrieFAQ";
import IndustrieFinalCTA, {
  defaultReassurances as hubFinalReassurances,
} from "@/components/industrie/IndustrieFinalCTA";

// --- Logistique ---------------------------------------------------------------
import LogistiqueHero, {
  defaultStats as logiHeroStats,
} from "@/components/industries/LogistiqueHero";
import LogistiqueProblem, {
  defaultFrictions as logiFrictions,
} from "@/components/industries/LogistiqueProblem";
import LogistiqueSolution, {
  defaultComparisons as logiComparisons,
  defaultKeyPoints as logiKeyPoints,
} from "@/components/industries/LogistiqueSolution";
import LogistiqueBenefits, {
  defaultBenefitCards as logiBenefitCards,
} from "@/components/industries/LogistiqueBenefits";
import LogistiqueApplications, {
  defaultWarehouseTypes as logiWarehouseTypes,
} from "@/components/industries/LogistiqueApplications";
import LogistiqueFAQ from "@/components/industries/LogistiqueFAQ";
import LogistiqueFinalCTA, {
  defaultReassurances as logiFinalReassurances,
} from "@/components/industries/LogistiqueFinalCTA";
import LogistiqueSEOContent from "@/components/industries/LogistiqueSEOContent";

// --- Distribution ----------------------------------------------------------------
import DistributionHero, {
  defaultStats as distHeroStats,
} from "@/components/industries/DistributionHero";
import DistributionProblem, {
  defaultProblems as distProblems,
} from "@/components/industries/DistributionProblem";
import DistributionSolution, {
  defaultComparisons as distComparisons,
  defaultKeyPoints as distKeyPoints,
} from "@/components/industries/DistributionSolution";
import DistributionValueDurability, {
  defaultOptions as distDurabilityOptions,
} from "@/components/industries/DistributionValueDurability";
import DistributionCEE from "@/components/industries/DistributionCEE";
import DistributionBenefits, {
  defaultBenefits as distBenefitCards,
} from "@/components/industries/DistributionBenefits";
import DistributionApplications, {
  defaultSurfaces as distSurfaces,
} from "@/components/industries/DistributionApplications";
import DistributionMethod, {
  defaultSteps as distMethodSteps,
} from "@/components/industries/DistributionMethod";
import DistributionProof, {
  defaultFigures as distProofFigures,
} from "@/components/industries/DistributionProof";
import DistributionFAQ from "@/components/industries/DistributionFAQ";
import DistributionFinalCTA, {
  defaultReassurances as distFinalReassurances,
} from "@/components/industries/DistributionFinalCTA";
import DistributionSEOContent from "@/components/industries/DistributionSEOContent";

// ---------------------------------------------------------------------------
// Helpers de mapping
// ---------------------------------------------------------------------------

/** Décode les entités HTML courantes des champs WP. */
const decode = (s: string): string =>
  s
    .replace(/&nbsp;/g, " ")
    .replace(/&amp;/g, "&")
    .replace(/&lt;/g, "<")
    .replace(/&gt;/g, ">")
    .replace(/&quot;/g, '"')
    .replace(/&#0?39;/g, "'")
    // wptexturize : apostrophes typographiques — les littéraux d'origine du
    // site utilisent l'apostrophe droite, on la restaure pour la parité pixel.
    .replace(/&rsquo;|&#8217;/g, "'")
    .replace(/&lsquo;|&#8216;/g, "'");

/** Texte brut d'un fragment HTML (balises supprimées). */
const text = (html?: string | null): string | undefined => {
  if (!html) return undefined;
  const t = decode(html.replace(/<[^>]+>/g, "")).trim();
  return t || undefined;
};

/** Valeur non vide ou undefined. */
const val = (s?: string | null): string | undefined =>
  s && s.trim() ? s : undefined;

/** Titre plain-text : convention *accent* retirée. */
const plain = (titre?: string | null): string | undefined =>
  titre ? val(titre.replace(/\*/g, "")) : undefined;

/**
 * Titre 2 lignes : « Tête *accent* » → Tête + <br/> + <span class>accent</span>
 * (markup des H2 originaux des sections industrie).
 */
const accentTwoLine = (
  titre: string | null | undefined,
  spanClass: string,
): ReactNode | undefined => {
  if (!titre || !titre.includes("*")) return plain(titre);
  const segments = parseAccent(titre);
  return (
    <>
      {segments.map((s, i) =>
        s.accent ? (
          <Fragment key={i}>
            <br />
            <span className={spanClass}>{s.text}</span>
          </Fragment>
        ) : (
          <Fragment key={i}>{s.text.replace(/\s+$/, "")}</Fragment>
        ),
      )}
    </>
  );
};

/**
 * Titre inline : « Tête *accent* » → Tête + <span class> accent</span>
 * (espace DANS le span, markup d'IndustrieMethod).
 */
const accentInline = (
  titre: string | null | undefined,
  spanClass: string,
): ReactNode | undefined => {
  if (!titre || !titre.includes("*")) return plain(titre);
  const segments = parseAccent(titre);
  return (
    <>
      {segments.map((s, i) =>
        s.accent ? (
          <span key={i} className={spanClass}> {s.text}</span>
        ) : (
          <Fragment key={i}>{s.text.replace(/\s+$/, "")}</Fragment>
        ),
      )}
    </>
  );
};

/**
 * Titre avec span au milieu, espaces conservés tels quels :
 * « Le moins cher *coûte plus cher* sur… » (DistributionValueDurability).
 */
const accentSpan = (
  titre: string | null | undefined,
  spanClass: string,
): ReactNode | undefined => {
  if (!titre || !titre.includes("*")) return plain(titre);
  const segments = parseAccent(titre);
  return (
    <>
      {segments.map((s, i) =>
        s.accent ? (
          <span key={i} className={spanClass}>{s.text}</span>
        ) : (
          <Fragment key={i}>{s.text}</Fragment>
        ),
      )}
    </>
  );
};

/** « Tête, suite. » → Tête, + <br/> + suite. (H2 des sections bénéfices). */
const commaBreak = (titre?: string | null): ReactNode | undefined => {
  const t = plain(titre);
  if (!t) return undefined;
  const idx = t.indexOf(", ");
  if (idx < 0) return t;
  return (
    <>
      {t.slice(0, idx + 1)}
      <br />
      {t.slice(idx + 2)}
    </>
  );
};

/** « Tête : suite. » → Tête : + <br/> + <span muted>suite.</span> (H2 SEO). */
const colonBreak = (
  titre: string | null | undefined,
  spanClass: string,
): ReactNode | undefined => {
  const t = plain(titre);
  if (!t) return undefined;
  const idx = t.indexOf(" : ");
  if (idx < 0) return t;
  return (
    <>
      {t.slice(0, idx + 2)}
      <br />
      <span className={spanClass}>{t.slice(idx + 3)}</span>
    </>
  );
};

/**
 * Intro avec emphase : « … <strong>80°C en plein été</strong>. … » →
 * texte + <strong className>…</strong> (markup des intros originales).
 * Sans balise <strong>, retourne la chaîne telle quelle.
 */
const strongIntro = (
  intro: string | null | undefined,
  strongClass: string,
): ReactNode | undefined => {
  const v = val(intro);
  if (!v) return undefined;
  if (!v.includes("<strong")) return decode(v);
  const parts = v.split(/<strong[^>]*>([\s\S]*?)<\/strong>/);
  return (
    <>
      {parts.map((p, i) =>
        i % 2 === 1 ? (
          <strong key={i} className={strongClass}>
            {decode(p)}
          </strong>
        ) : (
          <Fragment key={i}>{decode(p)}</Fragment>
        ),
      )}
    </>
  );
};

/** Paires <h3>titre</h3><p>texte</p> d'un wysiwyg. */
const h3pPairs = (html?: string | null): { title: string; desc: string }[] =>
  html
    ? [...html.matchAll(/<h3[^>]*>([\s\S]*?)<\/h3>\s*<p[^>]*>([\s\S]*?)<\/p>/g)].map((m) => ({
        title: text(m[1]) ?? "",
        desc: text(m[2]) ?? "",
      }))
    : [];

/** Paires <h3>titre</h3><ul>…</ul> d'un wysiwyg (cartes solution). */
const h3UlPairs = (html?: string | null): { title: string; points: string[] }[] =>
  html
    ? [...html.matchAll(/<h3[^>]*>([\s\S]*?)<\/h3>\s*<ul>([\s\S]*?)<\/ul>/g)].map((m) => ({
        title: text(m[1]) ?? "",
        points: [...m[2].matchAll(/<li[^>]*>([\s\S]*?)<\/li>/g)]
          .map((li) => text(li[1]) ?? "")
          .filter(Boolean),
      }))
    : [];

/** Inner HTML du premier <p> d'un wysiwyg. */
const firstP = (html?: string | null): string | undefined => {
  const m = html?.match(/<p[^>]*>([\s\S]*?)<\/p>/);
  return m ? m[1] : undefined;
};

/** ReactNode HTML brut (corps SEO : <strong>, <a>…). */
const htmlNode = (s?: string | null): ReactNode | undefined =>
  s ? <span dangerouslySetInnerHTML={{ __html: sanitizeWpHtml(s) }} /> : undefined;

/** Id YouTube depuis une URL watch / youtu.be / embed. */
const youtubeId = (url?: string | null): string | undefined => {
  if (!url) return undefined;
  const m =
    url.match(/[?&]v=([\w-]{6,})/) ??
    url.match(/youtu\.be\/([\w-]{6,})/) ??
    url.match(/\/embed\/([\w-]{6,})/);
  return m?.[1];
};

/** Merge index par index sur un squelette par défaut (icônes conservées). */
function mergeOnto<TDef, TIn>(
  defaults: TDef[],
  items: TIn[],
  merge: (def: TDef, item: TIn) => TDef,
): TDef[] | undefined {
  if (!items.length) return undefined;
  return defaults.map((def, i) => (items[i] ? merge(def, items[i]) : def));
}

/** FAQ : questions/réponses WP (réponse wysiwyg) → { q, a } plain text. */
const mapFaqs = (section: FaqBlock): { q: string; a: string }[] | undefined => {
  const faqs = arr(section.questions)
    .map((q) => ({ q: val(q.question) ?? "", a: text(q.reponse) ?? "" }))
    .filter((f) => f.q && f.a);
  return faqs.length ? faqs : undefined;
};

// ---------------------------------------------------------------------------
// Adaptateurs — hub /industrie
// ---------------------------------------------------------------------------

/**
 * H1 du hero hub : « Réduisez la *chaleur*, maîtrisez vos coûts. » →
 * accent souligné + virgule + <br/> + seconde ligne en muted (markup original).
 */
const hubHeroTitre = (titre?: string | null): ReactNode | undefined => {
  if (!titre || !titre.includes("*")) return plain(titre);
  const segments = parseAccent(titre);
  return (
    <>
      {segments.map((s, i) => {
        if (s.accent) {
          return (
            <span
              key={i}
              className="underline decoration-primary/40 decoration-[4px] underline-offset-[10px]"
            >
              {s.text}
            </span>
          );
        }
        const m = i > 0 ? s.text.match(/^([,.]?)\s+([\s\S]*)$/) : null;
        if (m && m[2]) {
          return (
            <Fragment key={i}>
              {m[1]}
              <br />
              <span className="text-foreground/45">{m[2]}</span>
            </Fragment>
          );
        }
        return <Fragment key={i}>{s.text}</Fragment>;
      })}
    </>
  );
};

const IndustrieHeroAdapter = (section: HeroBlock) => {
  const img = mapImage(section.image);
  const stats = arr(section.stats);
  return (
    <IndustrieHero
      eyebrow={val(section.eyebrow)}
      titre={hubHeroTitre(section.titre)}
      lead={val(section.lead)}
      imageSrc={img?.sourceUrl}
      imageAlt={img?.altText || undefined}
      proofs={mergeOnto(hubProofs, stats, (def, s) => ({
        ...def,
        value: val(s.value) ?? def.value,
        label: val(s.label) ?? def.label,
      }))}
      badgeTitle={val(arr(section.badges)[0]?.texte)}
      ctaPrimaireLabel={val(section.ctaPrimaire?.label)}
      ctaPrimaireHref={val(section.ctaPrimaire?.lien)}
      ctaSecondaireLabel={val(section.ctaSecondaire?.label)}
      ctaSecondaireHref={val(section.ctaSecondaire?.lien)}
    />
  );
};

const IndustrieProblemAdapter = (section: GrilleCardsBlock) => (
  <IndustrieProblem
    badge={val(section.entete?.badge)}
    titre={accentTwoLine(section.entete?.titre, "text-foreground/40")}
    intro={val(section.entete?.intro)}
    frictions={mergeOnto(hubFrictions, arr(section.cards), (def, c) => ({
      ...def,
      title: val(c.titre) ?? def.title,
      desc: val(c.texte) ?? def.desc,
    }))}
  />
);

const IndustriePrincipleAdapter = (section: TexteImageBlock) => {
  const pairs = h3pPairs(section.contenu);
  const img = mapImage(section.image);
  return (
    <IndustriePrinciple
      badge={val(section.entete?.badge)}
      titre={plain(section.entete?.titre)}
      intro={val(section.entete?.intro)}
      classicTitle={pairs[0]?.title || undefined}
      classicPoint={pairs[0]?.desc || undefined}
      coolTitle={pairs[1]?.title || undefined}
      coolPoint={pairs[1]?.desc || undefined}
      coolImageSrc={img?.sourceUrl}
      coolImageAlt={img?.altText || undefined}
      benefits={mergeOnto(hubPrincipleBenefits, arr(section.liste), (def, l) => ({
        ...def,
        label: val(l.texte) ?? def.label,
      }))}
      ctaLabel={val(section.cta?.label)}
      ctaHref={val(section.cta?.lien)}
    />
  );
};

const IndustrieBenefitsAdapter = (section: ChiffresBlock & GrilleCardsBlock) => {
  // Deux rows WP alimentent ce composant : `chiffres` (métrique -40%) et
  // `grille_cards` sans entête (5 cartes). Ne poser `origine` que sur UNE
  // des deux rows pour éviter un double rendu.
  if (arr(section.cards).length) {
    return (
      <IndustrieBenefits
        benefits={mergeOnto(hubBenefitCards, arr(section.cards), (def, c) => ({
          ...def,
          title: val(c.titre) ?? def.title,
          desc: val(c.texte) ?? def.desc,
        }))}
      />
    );
  }
  const figure = arr(section.figures)[0];
  const m = figure?.value?.match(/^([\s\S]*?)(%)\s*$/);
  return (
    <IndustrieBenefits
      badge={val(section.entete?.badge)}
      titre={accentTwoLine(section.entete?.titre, "text-foreground/40")}
      intro={val(section.entete?.intro)}
      sublabel={val(figure?.sublabel)}
      metricMain={m ? m[1] : val(figure?.value)}
      metricSuffix={figure?.value ? (m ? "%" : "") : undefined}
      metricLabel={val(figure?.label)}
    />
  );
};

const IndustrieApplicationsAdapter = (section: GrilleCardsBlock) => (
  <IndustrieApplications
    badge={val(section.entete?.badge)}
    titre={accentTwoLine(section.entete?.titre, "text-foreground/40")}
    intro={val(section.entete?.intro)}
    applications={mergeOnto(hubApplications, arr(section.cards), (def, c) => {
      const img = mapImage(c.image);
      return {
        ...def,
        name: val(c.titre) ?? def.name,
        line: val(c.texte) ?? def.line,
        img: img?.sourceUrl ?? def.img,
      };
    })}
    ctaLabel={val(section.transition)}
  />
);

const IndustrieMethodAdapter = (section: EtapesBlock) => {
  // reassurance = "Titre — desc\nTitre — desc" (2 blocs réassurance).
  const reassuranceLines = (section.reassurance ?? "")
    .split("\n")
    .map((l) => l.trim())
    .filter(Boolean)
    .map((l) => {
      const idx = l.indexOf("—");
      if (idx < 0) return { title: l, desc: "" };
      return { title: l.slice(0, idx).trim(), desc: l.slice(idx + 1).trim() };
    });
  return (
    <IndustrieMethod
      badge={val(section.entete?.badge)}
      titre={accentInline(section.entete?.titre, "text-foreground/35")}
      intro={val(section.entete?.intro)}
      steps={mergeOnto(hubMethodSteps, arr(section.etapes), (def, e) => ({
        ...def,
        title: val(e.titre) ?? def.title,
        desc: val(e.texte) ?? def.desc,
      }))}
      reassurances={mergeOnto(hubMethodReassurances, reassuranceLines, (def, r) => ({
        ...def,
        title: r.title || def.title,
        desc: r.desc || def.desc,
      }))}
    />
  );
};

/**
 * IndustrieProof est aussi rendu sur les pages secteur (Logistique :
 * `<IndustrieProof sector="logistique" />` dans la page d'origine) — le
 * secteur filtre les références mises en avant. Une clé de registry par
 * secteur (la row WP `references_grille` voisine est absorbée).
 */
const industrieProofAdapter =
  (sector?: "industrie" | "logistique") => (section: ChiffresBlock) => (
    <IndustrieProof
      sector={sector}
      badge={val(section.entete?.badge)}
      titre={accentTwoLine(section.entete?.titre, "text-teal-vivid")}
      intro={val(section.entete?.intro)}
      figures={mergeOnto(hubProofFigures, arr(section.figures), (def, f) => ({
        ...def,
        value: val(f.value) ?? def.value,
        label: val(f.label) ?? def.label,
      }))}
    />
  );

const IndustrieProofAdapter = industrieProofAdapter();
const IndustrieProofLogistiqueAdapter = industrieProofAdapter("logistique");

const IndustrieFAQAdapter = (section: FaqBlock) => (
  <IndustrieFAQ
    badge={val(section.entete?.badge)}
    titre={accentTwoLine(section.entete?.titre, "text-foreground/40")}
    intro={val(section.entete?.intro)}
    faqs={mapFaqs(section)}
    ctaLabel={val(section.cta?.label)}
    ctaHref={val(section.cta?.lien)}
  />
);

const IndustrieFinalCTAAdapter = (section: CtaBlock) => (
  <IndustrieFinalCTA
    titre={plain(section.titre)}
    texte={val(section.texte)}
    ctaPrimaireLabel={val(section.ctaPrimaire?.label)}
    ctaPrimaireHref={val(section.ctaPrimaire?.lien)}
    ctaSecondaireLabel={val(section.ctaSecondaire?.label)}
    ctaSecondaireHref={val(section.ctaSecondaire?.lien)}
    reassurances={mergeOnto(hubFinalReassurances, arr(section.reassurances), (def, r) => ({
      ...def,
      title: val(r.texte) ?? def.title,
    }))}
  />
);

// ---------------------------------------------------------------------------
// Adaptateurs — Logistique
// ---------------------------------------------------------------------------

const LogistiqueHeroAdapter = (section: HeroBlock) => {
  const img = mapImage(section.image);
  return (
    <LogistiqueHero
      eyebrow={val(section.eyebrow)}
      titre={plain(section.titre)}
      lead={val(section.lead)}
      imageSrc={img?.sourceUrl}
      imageAlt={img?.altText || undefined}
      stats={mergeOnto(logiHeroStats, arr(section.stats), (def, s) => ({
        ...def,
        value: val(s.value) ?? def.value,
        label: val(s.label) ?? def.label,
      }))}
      ctaLabel={val(section.ctaPrimaire?.label)}
    />
  );
};

const LogistiqueProblemAdapter = (section: GrilleCardsBlock) => (
  <LogistiqueProblem
    badge={val(section.entete?.badge)}
    titre={accentTwoLine(section.entete?.titre, "text-foreground/40")}
    intro={strongIntro(section.entete?.intro, "font-semibold text-foreground")}
    frictions={mergeOnto(logiFrictions, arr(section.cards), (def, c) => {
      const img = mapImage(c.image);
      return {
        ...def,
        title: val(c.titre) ?? def.title,
        desc: val(c.texte) ?? def.desc,
        image: img?.sourceUrl ?? def.image,
        imageAlt: img?.altText || def.imageAlt,
      };
    })}
  />
);

/** « Toiture cool roof — jusqu'à 90% du rayonnement réfléchi » → métrique. */
const solutionCard = (pair?: {
  title: string;
  points: string[];
}): { title?: string; metric?: string; metricLabel?: string; points?: string[] } => {
  if (!pair) return {};
  const [head, tail] = pair.title.split("—").map((s) => s.trim());
  const m = tail?.match(/^(jusqu'à\s+\S+)\s+([\s\S]+)$/i);
  return {
    title: head || undefined,
    metric: m ? m[1] : undefined,
    metricLabel: m ? m[2] : undefined,
    points: pair.points.length ? pair.points : undefined,
  };
};

const LogistiqueSolutionAdapter = (section: TexteImageBlock) => {
  const pairs = h3UlPairs(section.contenu);
  const img = mapImage(section.image);
  const cards = pairs.length
    ? logiComparisons.map((def, i) => {
        const parsed = solutionCard(pairs[i]);
        const isCool = def.tone === "cool";
        return {
          ...def,
          title: parsed.title ?? def.title,
          metric: parsed.metric ?? def.metric,
          metricLabel: parsed.metricLabel ?? def.metricLabel,
          points: parsed.points ?? def.points,
          image: isCool && img ? img.sourceUrl : def.image,
          alt: isCool && img?.altText ? img.altText : def.alt,
        };
      })
    : undefined;
  return (
    <LogistiqueSolution
      badge={val(section.entete?.badge)}
      titre={accentTwoLine(section.entete?.titre, "text-white/45")}
      intro={strongIntro(section.entete?.intro, "font-semibold text-white")}
      comparisons={cards}
      keyPoints={mergeOnto(logiKeyPoints, arr(section.liste), (def, l) => ({
        ...def,
        label: val(l.texte) ?? def.label,
      }))}
    />
  );
};

const LogistiqueBenefitsAdapter = (section: ChiffresBlock & GrilleCardsBlock) => {
  // Deux rows WP : `chiffres` (-10°C) et `grille_cards` (4 impacts). Ne poser
  // `origine` que sur UNE des deux rows pour éviter un double rendu.
  if (arr(section.cards).length) {
    return (
      <LogistiqueBenefits
        cardsTitle={plain(section.entete?.titre)}
        cardsIntro={val(section.entete?.intro)}
        benefitCards={mergeOnto(logiBenefitCards, arr(section.cards), (def, c) => ({
          ...def,
          title: val(c.titre) ?? def.title,
          desc: val(c.texte) ?? def.desc,
        }))}
      />
    );
  }
  const figure = arr(section.figures)[0];
  const [metricLabel, footnote] = (figure?.label ?? "").split(" — ").map((s) => s.trim());
  return (
    <LogistiqueBenefits
      badge={val(section.entete?.badge)}
      titre={commaBreak(section.entete?.titre)}
      intro={val(section.entete?.intro)}
      sublabel={val(figure?.sublabel)}
      metric={val(figure?.value)}
      metricLabel={val(metricLabel)}
      footnote={val(footnote)}
    />
  );
};

const LogistiqueApplicationsAdapter = (section: GrilleCardsBlock) => (
  <LogistiqueApplications
    badge={val(section.entete?.badge)}
    titre={plain(section.entete?.titre)}
    intro={val(section.entete?.intro)}
    warehouseTypes={mergeOnto(logiWarehouseTypes, arr(section.cards), (def, c) => {
      const img = mapImage(c.image);
      return {
        ...def,
        title: val(c.titre) ?? def.title,
        desc: val(c.texte) ?? def.desc,
        image: img?.sourceUrl ?? def.image,
      };
    })}
  />
);

const LogistiqueFAQAdapter = (section: FaqBlock) => (
  <LogistiqueFAQ
    badge={val(section.entete?.badge)}
    titre={accentTwoLine(section.entete?.titre, "text-foreground/40")}
    intro={val(section.entete?.intro)}
    faqs={mapFaqs(section)}
    ctaLabel={val(section.cta?.label)}
    ctaHref={val(section.cta?.lien)}
  />
);

const LogistiqueFinalCTAAdapter = (section: CtaBlock) => (
  <LogistiqueFinalCTA
    titre={plain(section.titre)}
    texte={val(section.texte)}
    ctaPrimaireLabel={val(section.ctaPrimaire?.label)}
    ctaPrimaireHref={val(section.ctaPrimaire?.lien)}
    ctaSecondaireLabel={val(section.ctaSecondaire?.label)}
    ctaSecondaireHref={val(section.ctaSecondaire?.lien)}
    reassurances={mergeOnto(logiFinalReassurances, arr(section.reassurances), (def, r) => ({
      ...def,
      title: val(r.texte) ?? def.title,
    }))}
  />
);

const LogistiqueSEOContentAdapter = (section: ContenuSeoBlock) => {
  const h2 = section.intro?.match(/<h2[^>]*>([\s\S]*?)<\/h2>/);
  const secs = arr(section.sections)
    .map((s) => ({ title: val(s.titre) ?? "", body: htmlNode(firstP(s.contenu)) }))
    .filter((s) => s.title && s.body);
  const sources = arr(section.sources)
    .map((s) => ({ label: val(s.label) ?? "", href: s.url || undefined }))
    .filter((s) => s.label);
  return (
    <LogistiqueSEOContent
      titre={h2 ? colonBreak(text(h2[1]), "text-foreground/42") : undefined}
      sections={secs.length ? secs : undefined}
      sourceLinks={sources.length ? sources : undefined}
    />
  );
};

// ---------------------------------------------------------------------------
// Adaptateurs — Distribution
// ---------------------------------------------------------------------------

const DistributionHeroAdapter = (section: HeroBlock) => {
  const img = mapImage(section.image);
  return (
    <DistributionHero
      eyebrow={val(section.eyebrow)}
      titre={plain(section.titre)}
      lead={val(section.lead)}
      imageSrc={img?.sourceUrl}
      imageAlt={img?.altText || undefined}
      stats={mergeOnto(distHeroStats, arr(section.stats), (def, s) => ({
        ...def,
        value: val(s.value) ?? def.value,
        label: val(s.label) ?? def.label,
      }))}
      ctaLabel={val(section.ctaPrimaire?.label)}
    />
  );
};

const DistributionProblemAdapter = (section: GrilleCardsBlock) => (
  <DistributionProblem
    badge={val(section.entete?.badge)}
    titre={accentTwoLine(section.entete?.titre, "text-foreground/40")}
    intro={val(section.entete?.intro)}
    problems={mergeOnto(distProblems, arr(section.cards), (def, c) => {
      const img = mapImage(c.image);
      return {
        ...def,
        title: val(c.titre) ?? def.title,
        desc: val(c.texte) ?? def.desc,
        image: img?.sourceUrl ?? def.image,
        alt: img?.altText || def.alt,
      };
    })}
    transition={val(section.transition)}
  />
);

const DistributionSolutionAdapter = (section: TexteImageBlock) => {
  const pairs = h3UlPairs(section.contenu);
  const img = mapImage(section.image);
  const cards = pairs.length
    ? distComparisons.map((def, i) => {
        const parsed = solutionCard(pairs[i]);
        const isCool = def.tone === "cool";
        return {
          ...def,
          title: parsed.title ?? def.title,
          metric: parsed.metric ?? def.metric,
          metricLabel: parsed.metricLabel ?? def.metricLabel,
          points: parsed.points ?? def.points,
          image: isCool && img ? img.sourceUrl : def.image,
          alt: isCool && img?.altText ? img.altText : def.alt,
        };
      })
    : undefined;
  return (
    <DistributionSolution
      badge={val(section.entete?.badge)}
      titre={accentTwoLine(section.entete?.titre, "text-white/45")}
      intro={val(section.entete?.intro)}
      comparisons={cards}
      keyPoints={mergeOnto(distKeyPoints, arr(section.liste), (def, l) => ({
        ...def,
        label: val(l.texte) ?? def.label,
      }))}
      transition={val(section.note)}
    />
  );
};

const tableRows = (section: TableauComparatifBlock): string[][] =>
  arr(section.lignesComparatif).map((l) => arr(l.cellules).map((c) => c.texte ?? ""));

const DistributionValueDurabilityAdapter = (section: TableauComparatifBlock) => {
  const rows = tableRows(section);
  const headers = arr(section.entetesColonnes).map((c) => c.texte ?? "");
  const subtitleRow = rows.find((r) => !r[0]?.trim() && (r[1] || r[2]));
  const findRow = (re: RegExp) => rows.find((r) => re.test(r[0] ?? ""));
  const priceRow = findRow(/prix/i);
  const lifeRow = findRow(/tenue/i);
  const costRow = findRow(/10 ans/i);
  const pointRows = rows.filter((r) => r[0]?.trim() === "-");
  const options = rows.length
    ? distDurabilityOptions.map((def, i) => {
        const col = i + 1;
        const points = pointRows.map((r) => r[col]).filter(Boolean);
        return {
          ...def,
          title: val(headers[col]) ?? def.title,
          subtitle: val(subtitleRow?.[col]) ?? def.subtitle,
          price: val(priceRow?.[col]) ?? def.price,
          life: val(lifeRow?.[col]) ?? def.life,
          realCost: val(costRow?.[col]) ?? def.realCost,
          points: points.length ? points : def.points,
        };
      })
    : undefined;
  return (
    <DistributionValueDurability
      badge={val(section.entete?.badge)}
      titre={accentSpan(section.entete?.titre, "text-foreground")}
      intro={val(section.entete?.intro)}
      options={options}
      note={val(section.texteApres)}
    />
  );
};

const DistributionCEEAdapter = (section: TableauComparatifBlock) => {
  const rows = tableRows(section);
  const zones = rows
    .filter((r) => r[0]?.trim())
    .map((r) => ({ zone: r[0], regions: r[1] ?? "", prime: r[2] ?? "" }));
  const lines = (section.texteApres ?? "")
    .split("\n")
    .map((l) => l.trim())
    .filter(Boolean);
  return (
    <DistributionCEE
      badge={val(section.entete?.badge)}
      titre={accentTwoLine(section.entete?.titre, "text-foreground/42")}
      intro={val(section.entete?.intro)}
      zones={zones.length ? zones : undefined}
      zonesNote={val(lines[0])}
      retenirNote={val(lines[1]?.replace(/^À retenir\s*:\s*/, ""))}
      transition={val(lines[2])}
      ctaLabel={val(section.cta?.label)}
    />
  );
};

const DistributionBenefitsAdapter = (section: ChiffresBlock & GrilleCardsBlock) => {
  // Deux rows WP : `chiffres` (-8 à -10°C) et `grille_cards` (5 cartes). Ne
  // poser `origine` que sur UNE des deux rows pour éviter un double rendu.
  if (arr(section.cards).length) {
    return (
      <DistributionBenefits
        cardsTitle={plain(section.entete?.titre)}
        cardsIntro={val(section.entete?.intro)}
        benefits={mergeOnto(distBenefitCards, arr(section.cards), (def, c) => ({
          ...def,
          title: val(c.titre) ?? def.title,
          desc: val(c.texte) ?? def.desc,
        }))}
      />
    );
  }
  const figure = arr(section.figures)[0];
  return (
    <DistributionBenefits
      badge={val(section.entete?.badge)}
      titre={commaBreak(section.entete?.titre)}
      intro={val(section.entete?.intro)}
      sublabel={val(figure?.sublabel)}
      metric={val(figure?.value)}
      metricLabel={val(figure?.label)}
    />
  );
};

const DistributionApplicationsAdapter = (section: GrilleCardsBlock) => (
  <DistributionApplications
    badge={val(section.entete?.badge)}
    titre={plain(section.entete?.titre)}
    intro={val(section.entete?.intro)}
    surfaces={mergeOnto(distSurfaces, arr(section.cards), (def, c) => {
      const img = mapImage(c.image);
      return {
        ...def,
        title: val(c.titre) ?? def.title,
        desc: val(c.texte) ?? def.desc,
        image: img?.sourceUrl ?? def.image,
      };
    })}
    transition={val(section.transition)}
  />
);

/** « 4 étapes. Magasin ouvert. *Zéro interruption.* » → markup original. */
const distMethodTitre = (titre?: string | null): ReactNode | undefined => {
  if (!titre || !titre.includes("*")) return plain(titre);
  const segments = parseAccent(titre);
  return (
    <>
      {segments.map((s, i) => {
        if (s.accent) {
          return (
            <span key={i} className="text-foreground/30"> {s.text}</span>
          );
        }
        const sentences = s.text.trim().split(/(?<=\.)\s+/).filter(Boolean);
        return (
          <Fragment key={i}>
            {sentences.map((sentence, j) => (
              <Fragment key={j}>
                {j > 0 && <br />}
                {sentence}
              </Fragment>
            ))}
          </Fragment>
        );
      })}
    </>
  );
};

const DistributionMethodAdapter = (section: EtapesBlock) => {
  const lines = (section.reassurance ?? "")
    .split("\n")
    .map((l) => l.trim())
    .filter(Boolean);
  const id = youtubeId(section.videoUrl);
  return (
    <DistributionMethod
      badge={val(section.entete?.badge)}
      titre={distMethodTitre(section.entete?.titre)}
      intro={val(section.entete?.intro)}
      steps={mergeOnto(distMethodSteps, arr(section.etapes), (def, e) => ({
        ...def,
        title: val(e.titre) ?? def.title,
        text: val(e.texte) ?? def.text,
      }))}
      duration={val(lines[0])}
      bottomNote={val(lines[1])}
      videoEmbedUrl={id ? `https://www.youtube.com/embed/${id}?autoplay=1` : undefined}
    />
  );
};

const DistributionProofAdapter = (section: ChiffresBlock) => (
  <DistributionProof
    badge={val(section.entete?.badge)}
    titre={accentTwoLine(section.entete?.titre, "text-teal-vivid")}
    intro={val(section.entete?.intro)}
    figures={mergeOnto(distProofFigures, arr(section.figures), (def, f) => ({
      ...def,
      value: val(f.value) ?? def.value,
      label: val(f.label) ?? def.label,
    }))}
  />
);

const DistributionFAQAdapter = (section: FaqBlock) => (
  <DistributionFAQ
    badge={val(section.entete?.badge)}
    titre={accentTwoLine(section.entete?.titre, "text-foreground/40")}
    intro={val(section.entete?.intro)}
    faqs={mapFaqs(section)}
    ctaLabel={val(section.cta?.label)}
    ctaHref={val(section.cta?.lien)}
  />
);

const DistributionFinalCTAAdapter = (section: CtaBlock) => (
  <DistributionFinalCTA
    titre={plain(section.titre)}
    texte={val(section.texte)}
    ctaPrimaireLabel={val(section.ctaPrimaire?.label)}
    ctaPrimaireHref={val(section.ctaPrimaire?.lien)}
    ctaSecondaireLabel={val(section.ctaSecondaire?.label)}
    ctaSecondaireHref={val(section.ctaSecondaire?.lien)}
    reassurances={mergeOnto(distFinalReassurances, arr(section.reassurances), (def, r) => ({
      ...def,
      title: val(r.texte) ?? def.title,
    }))}
  />
);

const DistributionSEOContentAdapter = (section: ContenuSeoBlock) => {
  const h2 = section.intro?.match(/<h2[^>]*>([\s\S]*?)<\/h2>/);
  const introP = section.intro?.match(/<p[^>]*>([\s\S]*?)<\/p>/);
  const secs = arr(section.sections)
    .map((s) => ({ title: val(s.titre) ?? "", body: htmlNode(firstP(s.contenu)) }))
    .filter((s) => s.title && s.body);
  const sources = arr(section.sources)
    .map((s) => ({ label: val(s.label) ?? "", href: s.url || undefined }))
    .filter((s) => s.label);
  return (
    <DistributionSEOContent
      titre={h2 ? colonBreak(text(h2[1]), "text-foreground/42") : undefined}
      intro={introP ? text(introP[1]) : undefined}
      sections={secs.length ? secs : undefined}
      sourceLinks={sources.length ? sources : undefined}
    />
  );
};

// ---------------------------------------------------------------------------
// Registry — clé = nom de fichier du composant original.
// ---------------------------------------------------------------------------

// Adaptateurs hétérogènes selon les sections WordPress d'origine.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const origineIndustrie: Record<string, ComponentType<any>> = {
  // Hub /industrie
  IndustrieHero: IndustrieHeroAdapter,
  IndustrieProblem: IndustrieProblemAdapter,
  IndustriePrinciple: IndustriePrincipleAdapter,
  IndustrieBenefits: IndustrieBenefitsAdapter,
  IndustrieApplications: IndustrieApplicationsAdapter,
  IndustrieMethod: IndustrieMethodAdapter,
  IndustrieProof: IndustrieProofAdapter,
  IndustrieFAQ: IndustrieFAQAdapter,
  IndustrieFinalCTA: IndustrieFinalCTAAdapter,
  // Logistique
  LogistiqueHero: LogistiqueHeroAdapter,
  LogistiqueProblem: LogistiqueProblemAdapter,
  LogistiqueSolution: LogistiqueSolutionAdapter,
  LogistiqueBenefits: LogistiqueBenefitsAdapter,
  LogistiqueApplications: LogistiqueApplicationsAdapter,
  IndustrieProofLogistique: IndustrieProofLogistiqueAdapter,
  LogistiqueFAQ: LogistiqueFAQAdapter,
  LogistiqueFinalCTA: LogistiqueFinalCTAAdapter,
  LogistiqueSEOContent: LogistiqueSEOContentAdapter,
  // Distribution
  DistributionHero: DistributionHeroAdapter,
  DistributionProblem: DistributionProblemAdapter,
  DistributionSolution: DistributionSolutionAdapter,
  DistributionValueDurability: DistributionValueDurabilityAdapter,
  DistributionCEE: DistributionCEEAdapter,
  DistributionBenefits: DistributionBenefitsAdapter,
  DistributionApplications: DistributionApplicationsAdapter,
  DistributionMethod: DistributionMethodAdapter,
  DistributionProof: DistributionProofAdapter,
  DistributionFAQ: DistributionFAQAdapter,
  DistributionFinalCTA: DistributionFinalCTAAdapter,
  DistributionSEOContent: DistributionSEOContentAdapter,
};
