// Adaptateurs origine — famille "home". Une entrée par section de la page
// d'accueil, 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).
import { Fragment, type ComponentType, type ReactNode } from "react";
import { sanitizeWpHtml } from "@/lib/sanitizeHtml";
import { arr, mapImage, parseAccent } from "@/lib/wp/mappers";
import type {
  ChiffresBlock,
  CtaBlock,
  ContenuSeoBlock,
  EtapesBlock,
  FaqBlock,
  GrilleCardsBlock,
  GrilleSecteursBlock,
  HeroBlock,
  LogosBlock,
  TableauComparatifBlock,
  TemoignagesVideoBlock,
  TexteImageBlock,
} from "@/lib/wp/types";

import Hero from "@/components/Hero";
import ConstatSection from "@/components/ConstatSection";
import CoolRoofExplainerSection from "@/components/CoolRoofExplainerSection";
import WinterObjectionSection from "@/components/WinterObjectionSection";
import SectorsGrid from "@/components/SectorsGrid";
import SystemSection, { defaultLayers } from "@/components/SystemSection";
import PriceComparisonSection from "@/components/PriceComparisonSection";
import SocialProofSection from "@/components/SocialProofSection";
import PriceVSSection from "@/components/PriceVSSection";
import RSESection, { defaultItems as defaultRseItems } from "@/components/RSESection";
import Testimonials from "@/components/Testimonials";
import ProcessSection from "@/components/ProcessSection";
import ExpertiseSection, { defaultCards } from "@/components/ExpertiseSection";
import FAQSection from "@/components/FAQ";
import PressSection from "@/components/PressSection";
import CTASection from "@/components/CTASection";
import BlogPreview from "@/components/BlogPreview";
import SEOContent from "@/components/SEOContent";
import ApplicatorsSection from "@/components/ApplicatorsSection";

// ---------------------------------------------------------------------------
// 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, "'")
    .replace(/&(?:rsquo|lsquo|#8216|#8217);/g, "'")
    .replace(/&(?:rdquo|#8221);/g, "”")
    .replace(/&(?:ldquo|#8220);/g, "“")
    .replace(/&(?:ndash|#8211);/g, "–")
    .replace(/&(?:mdash|#8212);/g, "—")
    .replace(/&(?:hellip|#8230);/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;
};

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

/**
 * Reconstruit le markup de titre de l'original à partir de la convention
 * `*accent*` : les segments accent sont rendus dans un <span> portant la
 * classe EXACTE de l'original ; `br` insère le <br /> de l'original avant
 * le span (titres sur deux lignes).
 */
const accentTitle = (
  titre: string | null | undefined,
  spanClass: string,
  opts?: { br?: boolean },
): ReactNode | undefined => {
  if (!titre || !titre.includes("*")) return plain(titre);
  const segments = parseAccent(titre);
  return (
    <>
      {segments.map((s, i) =>
        s.accent ? (
          <Fragment key={i}>
            {opts?.br && i > 0 && <br />}
            <span className={spanClass}>{s.text}</span>
          </Fragment>
        ) : (
          <Fragment key={i}>{opts?.br ? s.text.replace(/\s+$/, "") : s.text}</Fragment>
        ),
      )}
    </>
  );
};

/** Contenus (innerHTML) des blocs <p> d'un wysiwyg. */
const pBlocks = (html?: string | null): string[] =>
  html ? [...html.matchAll(/<p[^>]*>([\s\S]*?)<\/p>/g)].map((m) => m[1]) : [];

/** 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]) ?? "",
      }))
    : [];

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

/** Ajoute la classe des <strong> de l'original au HTML WP. */
const withStrongClass = (html: string, cls: string): string =>
  html.replace(/<strong>/g, `<strong class="${cls}">`);

/**
 * Annule wptexturize sur un fragment HTML injecté tel quel : ramène les
 * guillemets/apostrophes typographiques aux caractères droits des littéraux
 * JSX d'origine (parité pixel avec le site pré-CMS).
 */
const detexturize = (html: string): string =>
  html
    .replace(/[‘’]|&(?:rsquo|lsquo|#8216|#8217);/g, "'")
    .replace(/[“”]|&(?:ldquo|rdquo|#8220|#8221);/g, '"');

/** 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];
};

/** Sépare le texte d'une étape de la citation « … » — auteur du seed. */
const splitExpert = (texte?: string | null): { description?: string; expert?: string } => {
  if (!texte) return {};
  const m = texte.match(/\n\n«\s*([\s\S]*?)\s*»/);
  if (!m || m.index === undefined) return { description: texte };
  return { description: texte.slice(0, m.index).trim(), expert: m[1].trim() };
};

/** "Titre — sous-titre" → [titre, sous-titre]. */
const splitDash = (s?: string | null): [string | undefined, string | undefined] => {
  if (!s) return [undefined, undefined];
  const idx = s.indexOf("—");
  if (idx < 0) return [s.trim() || undefined, undefined];
  return [s.slice(0, idx).trim() || undefined, s.slice(idx + 1).trim() || undefined];
};

// ---------------------------------------------------------------------------
// Adaptateurs
// ---------------------------------------------------------------------------

/** Hero original COMPLET (formulaire d'estimation inclus, codé en dur). */
const HeroAdapter = (section: HeroBlock) => {
  const img = mapImage(section.image);
  return (
    <Hero
      titre={plain(section.titre)}
      lead={
        section.lead ? (
          <span
            dangerouslySetInnerHTML={{
              __html: sanitizeWpHtml(withStrongClass(detexturize(section.lead), "text-white font-semibold")),
            }}
          />
        ) : undefined
      }
      imageSrc={img?.sourceUrl}
      imageAlt={img?.altText || undefined}
    />
  );
};

const ConstatAdapter = (section: TexteImageBlock) => {
  const img = mapImage(section.image);
  const pairs = h3pPairs(section.contenu);
  return (
    <ConstatSection
      titre={accentTitle(section.entete?.titre, "text-foreground/30")}
      blocs={pairs.length ? pairs : undefined}
      imageSrc={img?.sourceUrl}
      imageAlt={img?.altText || undefined}
      note={section.note ?? undefined}
    />
  );
};

const CoolRoofExplainerAdapter = (section: TexteImageBlock) => {
  const img = mapImage(section.image);
  const inner = pBlocks(section.contenu)[0];
  return (
    <CoolRoofExplainerSection
      titre={accentTitle(section.entete?.titre, "text-foreground/30")}
      paragraphe={
        inner ? (
          <span
            dangerouslySetInnerHTML={{
              __html: sanitizeWpHtml(withStrongClass(detexturize(inner), "text-foreground/80 font-semibold")),
            }}
          />
        ) : undefined
      }
      imageSrc={img?.sourceUrl}
      imageAlt={img?.altText || undefined}
      note={section.note ?? undefined}
      ctaLabel={section.cta?.label ?? undefined}
      ctaHref={section.cta?.lien ?? undefined}
    />
  );
};

const WinterObjectionAdapter = (section: TexteImageBlock) => {
  const img = mapImage(section.image);
  const blocks = pBlocks(section.contenu);
  // 1er <p> (en <strong>) = accroche, les suivants = paragraphes.
  const intro = blocks.length ? text(blocks[0]) : undefined;
  return (
    <WinterObjectionSection
      titre={accentTitle(section.entete?.titre, "text-teal-vivid", { br: true })}
      intro={intro}
      paragraphe1={blocks[1] ? text(blocks[1]) : undefined}
      paragraphe2={blocks[2] ? text(blocks[2]) : undefined}
      imageSrc={img?.sourceUrl}
      imageAlt={img?.altText || undefined}
    />
  );
};

const SectorsGridAdapter = (section: GrilleSecteursBlock) => (
  <SectorsGrid
    badge={section.entete?.badge ?? undefined}
    titre={accentTitle(section.entete?.titre, "text-foreground/30", { br: true })}
    intro={section.entete?.intro ?? undefined}
  />
);

const SystemAdapter = (section: EtapesBlock) => {
  const etapes = arr(section.etapes);
  const layers = etapes.length
    ? defaultLayers.map((layer, i) => {
        const e = etapes[i];
        if (!e) return layer;
        const { description, expert } = splitExpert(e.texte);
        return {
          ...layer,
          title: e.titre ?? layer.title,
          description: description ?? layer.description,
          expert: expert ?? layer.expert,
        };
      })
    : undefined;
  const img = etapes.map((e) => mapImage(e.image)).find(Boolean) ?? null;
  return (
    <SystemSection
      badge={section.entete?.badge ?? undefined}
      titre={accentTitle(section.entete?.titre, "text-foreground/30")}
      intro={section.entete?.intro ?? undefined}
      layers={layers}
      imageSrc={img?.sourceUrl}
      imageAlt={img?.altText || undefined}
    />
  );
};

const PriceComparisonAdapter = (section: TableauComparatifBlock) => {
  const cols = arr(section.entetesColonnes).map((c) => splitDash(c.texte));
  // cols[0] = colonne Covalba (logo affiché, seul le prix est repris).
  const prix = cols.length === 4 ? cols.map(([, p]) => p ?? "") : undefined;
  const colonnes = cols.length === 4 ? cols.slice(1).map(([n]) => n ?? "") : undefined;
  const lignes = arr(section.lignesComparatif);
  const features = lignes.length
    ? lignes.map((l) => {
        const c = arr(l.cellules).map((x) => x.texte ?? "");
        return {
          label: c[0] ?? "",
          covalba: c[1] === "oui",
          clim: c[2] === "oui",
          membrane: c[3] === "oui",
          refection: c[4] === "oui",
        };
      })
    : undefined;
  // texte_apres = "Titre de l'encart. Corps de l'encart…"
  let encartTitre: string | undefined;
  let encartTexte: string | undefined;
  if (section.texteApres) {
    const idx = section.texteApres.indexOf(". ");
    if (idx > 0) {
      encartTitre = section.texteApres.slice(0, idx + 1);
      encartTexte = section.texteApres.slice(idx + 2);
    } else {
      encartTexte = section.texteApres;
    }
  }
  return (
    <PriceComparisonSection
      badge={section.entete?.badge ?? undefined}
      titre={accentTitle(section.entete?.titre, "text-foreground/30")}
      intro={section.entete?.intro ?? undefined}
      features={features}
      prix={prix}
      colonnes={colonnes}
      encartTitre={encartTitre}
      encartTexte={encartTexte}
      ctaLabel={section.cta?.label ?? undefined}
      ctaHref={section.cta?.lien ?? undefined}
    />
  );
};

const SocialProofAdapter = (section: ChiffresBlock) => {
  const figures = arr(section.figures);
  const rating = figures[0];
  const stats = figures.slice(1).map((f) => ({
    value: f.value ?? "",
    label: f.label ?? "",
    sub: f.sublabel ?? "",
  }));
  return (
    <SocialProofSection
      ratingValue={rating?.value ?? undefined}
      ratingLabel={rating?.label ?? undefined}
      stats={stats.length ? stats : undefined}
    />
  );
};

const PriceVSAdapter = (section: TableauComparatifBlock) => {
  const cols = arr(section.entetesColonnes).map((c) => splitDash(c.texte));
  const [leftTitle, leftSub] = cols[0] ?? [undefined, undefined];
  const [rightTitle, rightSub] = cols[1] ?? [undefined, undefined];
  const lignes = arr(section.lignesComparatif).map((l) => arr(l.cellules).map((x) => x.texte ?? ""));
  // 3 premières lignes = valeurs labellisées, lignes "-" = puces.
  const valued = lignes.filter((c) => c[0] && c[0] !== "-");
  const bullets = lignes.filter((c) => !c[0] || c[0] === "-");
  const stripM2 = (s?: string) => (s ? s.replace(/\s*\/\s*m²\s*$/, "").replace(/\/m²$/, "") : undefined);
  const find = (label: string) => valued.find((c) => c[0].toLowerCase().includes(label));
  const prixRow = find("prix");
  const dureeRow = find("durée");
  const coutRow = find("coût");
  const leftPoints = bullets.map((c) => c[1]).filter(Boolean);
  const rightPoints = bullets.map((c) => c[2]).filter(Boolean);
  return (
    <PriceVSSection
      badge={section.entete?.badge ?? undefined}
      titre={accentTitle(section.entete?.titre, "text-foreground")}
      intro={section.entete?.intro ?? undefined}
      labelPrix={prixRow?.[0]}
      labelDuree={dureeRow?.[0]}
      labelCout={coutRow?.[0]}
      leftTitle={leftTitle}
      leftSub={leftSub}
      leftPrix={prixRow?.[1]}
      leftDuree={dureeRow?.[1]}
      leftCout={stripM2(coutRow?.[1])}
      leftPoints={leftPoints.length ? leftPoints : undefined}
      rightTitle={rightTitle}
      rightSub={rightSub}
      rightPrix={prixRow?.[2]}
      rightDuree={dureeRow?.[2]}
      rightCout={stripM2(coutRow?.[2])}
      rightPoints={rightPoints.length ? rightPoints : undefined}
      conclusion={section.texteApres ?? undefined}
      ctaLabel={section.cta?.label ?? undefined}
      ctaHref={section.cta?.lien ?? undefined}
    />
  );
};

const RSEAdapter = (section: TexteImageBlock) => {
  const img = mapImage(section.image);
  const parsed = strongBrItems(section.contenu);
  const items = parsed.length
    ? defaultRseItems.map((item, i) => {
        const p = parsed[i];
        if (!p) return item;
        return { ...item, title: p.title || item.title, text: p.text || item.text };
      })
    : undefined;
  return (
    <RSESection
      badge={section.entete?.badge ?? undefined}
      titre={accentTitle(section.entete?.titre, "text-foreground/30")}
      items={items}
      imageSrc={img?.sourceUrl}
      imageAlt={img?.altText || undefined}
    />
  );
};

const TestimonialsAdapter = (section: TemoignagesVideoBlock) => {
  const temoignages = arr(section.temoignages).map((t) => ({
    videoId: youtubeId(t.videoUrl) ?? "",
    quote: t.citation ?? "",
    name: t.nom ?? "",
    role: t.role ?? "",
    company: t.entreprise ?? "",
    context: t.contexte ?? "",
  }));
  return (
    <Testimonials
      badge={section.entete?.badge ?? undefined}
      titre={accentTitle(section.entete?.titre, "text-teal-vivid")}
      intro={section.entete?.intro ?? undefined}
      testimonials={temoignages.length ? temoignages : undefined}
    />
  );
};

const ProcessAdapter = (section: EtapesBlock) => {
  const etapes = arr(section.etapes);
  // Icônes/numéros : skeleton de l'original conservé côté composant
  // (ProcessSection est un Client Component, on ne lui passe que des données).
  const stepsContent = etapes.length
    ? etapes.map((e) => ({ title: e.titre ?? undefined, text: e.texte ?? undefined }))
    : undefined;
  const id = youtubeId(section.videoUrl);
  return (
    <ProcessSection
      badge={section.entete?.badge ?? undefined}
      titre={accentTitle(section.entete?.titre, "text-foreground/30")}
      intro={section.entete?.intro ?? undefined}
      stepsContent={stepsContent}
      reassurance={section.reassurance ?? undefined}
      videoEmbedUrl={id ? `https://www.youtube.com/embed/${id}?autoplay=1` : undefined}
    />
  );
};

const ExpertiseAdapter = (section: GrilleCardsBlock) => {
  const wpCards = arr(section.cards);
  // Icônes + carte mise en avant : skeleton de l'original conservé,
  // seuls les textes viennent de WP (champ `icone` WP ignoré).
  const cards = wpCards.length
    ? defaultCards.map((card, i) => {
        const c = wpCards[i];
        if (!c) return card;
        return { ...card, title: c.titre ?? card.title, text: c.texte ?? card.text };
      })
    : undefined;
  return (
    <ExpertiseSection
      badge={section.entete?.badge ?? undefined}
      titre={accentTitle(section.entete?.titre, "text-foreground/30")}
      intro={section.entete?.intro ?? undefined}
      cards={cards}
    />
  );
};

const FAQAdapter = (section: FaqBlock) => {
  const faqs = arr(section.questions).map((q) => ({
    q: q.question ?? "",
    a: q.reponse ?? "",
  }));
  return (
    <FAQSection
      badge={section.entete?.badge ?? undefined}
      titre={plain(section.entete?.titre)}
      intro={section.entete?.intro ?? undefined}
      faqs={faqs.length ? faqs : undefined}
      ctaLabel={section.cta?.label ?? undefined}
      ctaHref={section.cta?.lien ?? undefined}
    />
  );
};

const PressAdapter = (section: LogosBlock) => {
  const outlets = arr(section.logosCustom)
    .map((l) => {
      const img = mapImage(l.image);
      return {
        name: l.nom ?? img?.altText ?? "",
        logo: img?.sourceUrl ?? "",
        url: l.lien ?? undefined,
      };
    })
    .filter((o) => o.logo);
  return (
    <PressSection
      label={section.entete?.badge ?? undefined}
      outlets={outlets.length ? outlets : undefined}
    />
  );
};

const CTAAdapter = (section: CtaBlock) => (
  <CTASection
    title={plain(section.titre)}
    subtitle={section.texte ?? undefined}
    primaryLabel={section.ctaPrimaire?.label ?? undefined}
    primaryHref={section.ctaPrimaire?.lien ?? undefined}
    secondaryLabel={section.ctaSecondaire?.label ?? undefined}
    secondaryHref={section.ctaSecondaire?.lien ?? undefined}
  />
);

const BlogPreviewAdapter = (section: GrilleCardsBlock) => {
  const articles = arr(section.cards)
    .map((c) => {
      const img = mapImage(c.image);
      return {
        title: c.titre ?? "",
        category: c.texte ?? "",
        image: img?.sourceUrl ?? "",
        href: c.lien ?? undefined,
      };
    })
    .filter((a) => a.title);
  return (
    <BlogPreview
      badge={section.entete?.badge ?? undefined}
      titre={plain(section.entete?.titre)}
      articles={articles.length ? articles : undefined}
    />
  );
};

const SEOContentAdapter = (section: ContenuSeoBlock) => {
  const secs = arr(section.sections);
  const sectionsNode = secs.length ? (
    <>
      {secs.map((s, i) => (
        <div key={i}>
          <h3 className="font-satoshi text-2xl lg:text-3xl font-bold text-foreground mb-4">
            {s.titre ? decode(s.titre) : s.titre}
          </h3>
          <div
            className="space-y-4 [&_strong]:text-foreground [&_a]:text-primary [&_a]:font-semibold hover:[&_a]:underline [&_ul]:list-disc [&_ul]:pl-6 [&_ul]:space-y-3 [&_ol]:list-decimal [&_ol]:pl-6 [&_ol]:space-y-3 [&_table]:w-full [&_table]:text-sm [&_table]:border-collapse [&_table]:!mt-6 [&_th]:text-left [&_th]:font-satoshi [&_th]:font-bold [&_th]:px-4 [&_th]:py-3 [&_thead_th:first-child]:rounded-tl-xl [&_thead_th:last-child]:rounded-tr-xl [&_thead_tr]:bg-foreground [&_thead_tr]:text-white [&_tbody_tr:nth-child(odd)]:bg-white [&_tbody_tr:nth-child(even)]:bg-foreground/[0.03] [&_td]:px-4 [&_td]:py-3 [&_td]:border-b [&_td]:border-foreground/5 [&_td:first-child]:font-semibold [&_td:first-child]:text-foreground [&_td:nth-child(2)]:text-primary [&_td:nth-child(2)]:font-semibold [&_td:nth-child(n+3)]:text-foreground/70"
            dangerouslySetInnerHTML={{ __html: sanitizeWpHtml(detexturize(s.contenu ?? "")) }}
          />
        </div>
      ))}
    </>
  ) : undefined;
  const sources = arr(section.sources).map((s) => ({
    ref: s.label ?? "",
    url: s.url || undefined,
  }));
  return (
    <SEOContent
      intro={text(section.intro)}
      sections={sectionsNode}
      sources={sources.length ? sources : undefined}
    />
  );
};

const ApplicatorsAdapter = (section: TexteImageBlock) => {
  const img = mapImage(section.image);
  const benefits = arr(section.liste)
    .map((l) => l.texte ?? "")
    .filter(Boolean);
  return (
    <ApplicatorsSection
      badge={section.entete?.badge ?? undefined}
      title={plain(section.entete?.titre)}
      description={text(section.contenu)}
      benefits={benefits.length ? benefits : undefined}
      imageSrc={img?.sourceUrl}
      imageAlt={img?.altText || undefined}
      ctaLabel={section.cta?.label ?? undefined}
      ctaHref={section.cta?.lien ?? 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 origineHome: Record<string, ComponentType<any>> = {
  Hero: HeroAdapter,
  ConstatSection: ConstatAdapter,
  CoolRoofExplainerSection: CoolRoofExplainerAdapter,
  WinterObjectionSection: WinterObjectionAdapter,
  SectorsGrid: SectorsGridAdapter,
  SystemSection: SystemAdapter,
  PriceComparisonSection: PriceComparisonAdapter,
  SocialProofSection: SocialProofAdapter,
  PriceVSSection: PriceVSAdapter,
  RSESection: RSEAdapter,
  Testimonials: TestimonialsAdapter,
  ProcessSection: ProcessAdapter,
  ExpertiseSection: ExpertiseAdapter,
  FAQ: FAQAdapter,
  PressSection: PressAdapter,
  CTASection: CTAAdapter,
  BlogPreview: BlogPreviewAdapter,
  SEOContent: SEOContentAdapter,
  ApplicatorsSection: ApplicatorsAdapter,
};
