// Mapper inverse : sections WP du CPT `toiture` → RoofPageData consommé par le
// template d'origine (src/components/roof/RoofPageTemplate.tsx).
//
// Bijection inverse du mapping d'export (scripts/wp-seed/export-data.mts,
// buildToitureDoc) :
//   hero               → eyebrow / h1 / lead / heroImage / heroStats
//   barre_reassurance  → reassurance
//   grille_cards #1    → problem        grille_cards #2 → benefits
//   texte_image  #1    → solution       texte_image  #2 → compatibility
//   tableau_comparatif → roi.comparison tableau_situations → roi.situation
//   cta (mid)          → midCta         cta (final)        → finalCta
//   etapes             → method         chiffres           → proof
//   faq                → faq            contenu_seo        → seo
//
// Champ WP manquant/vide → valeur du RoofPageData codé (deep-merge champ à
// champ via roofPageBySlug : la donnée WP prime, le codé comble).

import {
  roofPageBySlug,
  type RoofPageData,
  type TableBlock,
  type TextCard,
} from "@/data/roofPages";

import { arr, mapImage } from "./mappers";
import type {
  BarreReassuranceBlock,
  ChiffresBlock,
  ContenuSeoBlock,
  CtaBlock,
  EtapesBlock,
  FaqBlock,
  GrilleCardsBlock,
  HeroBlock,
  TableauComparatifBlock,
  TableauSituationsBlock,
  TexteImageBlock,
  WpSection,
  WpToiture,
} from "./types";

/* ------------------------------------------------------------------ */
/* Identification des layouts (même convention que BlockRenderer)      */
/* ------------------------------------------------------------------ */

// Suffixes `...XxxLayout` du __typename WPGraphQL, triés par longueur
// décroissante : `TemoignagesVideoLayout` doit matcher avant `VideoLayout`.
const LAYOUT_SUFFIXES = [
  "TemoignagesVideoLayout",
  "BarreReassuranceLayout",
  "TableauComparatifLayout",
  "TableauSituationsLayout",
  "FicheChantierLayout",
  "GrilleCardsLayout",
  "ContenuSeoLayout",
  "TexteImageLayout",
  "ChiffresLayout",
  "EtapesLayout",
  "VideoLayout",
  "HeroLayout",
  "FaqLayout",
  "CtaLayout",
].sort((a, b) => b.length - a.length);

export type LayoutKey = (typeof LAYOUT_SUFFIXES)[number];

/** Suffixe `...XxxLayout` d'une section, ou null si layout non géré ici. */
export function layoutOf(section: WpSection): string | null {
  const typename = section?.__typename;
  if (!typename) return null;
  return LAYOUT_SUFFIXES.find((suffix) => typename.endsWith(suffix)) ?? null;
}

/** Sections d'un layout donné, dans l'ordre du flexible content. */
export function sectionsOf<T extends WpSection>(
  sections: WpSection[],
  layout: string
): T[] {
  return sections.filter((section) => layoutOf(section) === layout) as T[];
}

/* ------------------------------------------------------------------ */
/* Helpers texte (inverse des helpers p()/esc() de l'export)           */
/* ------------------------------------------------------------------ */

/**
 * Inverse de l'échappement minimal `esc` de l'export + de la texturisation
 * WordPress (wptexturize transforme `'` en `&rsquo;`, `"` en `&ldquo;`/`&rdquo;`,
 * etc. dans les champs wysiwyg). On restaure les caractères du littéral d'origine.
 */
function unescapeHtml(text: string): string {
  return text
    .replace(/&lt;/g, "<")
    .replace(/&gt;/g, ">")
    .replace(/&nbsp;|&#160;/g, " ")
    .replace(/&rsquo;|&lsquo;|&#821[67];|&#0?39;/g, "'")
    .replace(/&rdquo;|&ldquo;|&#822[01];|&quot;/g, '"')
    .replace(/&ndash;|&#8211;/g, "–")
    .replace(/&mdash;|&#8212;/g, "—")
    .replace(/&hellip;|&#8230;/g, "…")
    .replace(/&amp;/g, "&");
}

/** Contenus texte des `<p>…</p>` d'un champ wysiwyg (inverse de `p()`). */
export function paragraphsOf(html: string | null | undefined): string[] {
  if (!html) return [];
  const out: string[] = [];
  const re = /<p>([\s\S]*?)<\/p>/g;
  let match: RegExpExecArray | null;
  while ((match = re.exec(html)) !== null) {
    const text = unescapeHtml(match[1]).trim();
    if (text) out.push(text);
  }
  if (!out.length) {
    const text = stripHtml(html);
    if (text) out.push(text);
  }
  return out;
}

/** Texte brut d'un fragment HTML (balises retirées, entités décodées). */
export function stripHtml(html: string | null | undefined): string | undefined {
  if (!html) return undefined;
  const text = unescapeHtml(html.replace(/<[^>]*>/g, " "))
    .replace(/\s+/g, " ")
    .trim();
  return text || undefined;
}

/** Chaîne non vide → telle quelle ; null/undefined/vide → undefined (fallback codé). */
export function str(value: string | null | undefined): string | undefined {
  const text = value?.trim();
  return text ? value ?? undefined : undefined;
}

/* ------------------------------------------------------------------ */
/* Helpers de structures                                               */
/* ------------------------------------------------------------------ */

/** Repeater {titre, texte} → TextCard[] ; vide → undefined (fallback codé). */
function textCards(
  items: { titre?: string | null; texte?: string | null }[] | null | undefined
): TextCard[] | undefined {
  const rows = arr(items);
  if (!rows.length) return undefined;
  return rows.map((row) => ({ title: row.titre ?? "", text: row.texte ?? "" }));
}

/** Repeater {value, label} → figures ; vide → undefined (fallback codé). */
function valueLabels(
  items: { value?: string | null; label?: string | null }[] | null | undefined
): { value: string; label: string }[] | undefined {
  const rows = arr(items);
  if (!rows.length) return undefined;
  return rows.map((row) => ({ value: row.value ?? "", label: row.label ?? "" }));
}

/** Repeater {texte} → string[] ; vide → undefined (fallback codé). */
function textes(
  items: { texte?: string | null }[] | null | undefined
): string[] | undefined {
  const rows = arr(items);
  if (!rows.length) return undefined;
  return rows.map((row) => row.texte ?? "");
}

// En-têtes du tableau "situations" : constants côté données codées, absents du
// schéma WP (le layout tableau_situations n'a pas de colonnes paramétrables).
const SITUATION_HEADERS = [
  "Votre situation",
  "Ce qu'on recommande",
  "Pourquoi c'est le bon calcul",
];

/* ------------------------------------------------------------------ */
/* Mapper principal                                                    */
/* ------------------------------------------------------------------ */

export function toRoofPageData(toiture: WpToiture): RoofPageData {
  const slug = toiture.slug ?? "";
  const fb: RoofPageData | undefined = roofPageBySlug[slug];
  const sections = arr(toiture.sections?.sections);

  const hero = sectionsOf<HeroBlock>(sections, "HeroLayout")[0];
  const barre = sectionsOf<BarreReassuranceBlock>(
    sections,
    "BarreReassuranceLayout"
  )[0];
  const [grilleProblem, grilleBenefits] = sectionsOf<GrilleCardsBlock>(
    sections,
    "GrilleCardsLayout"
  );
  const [texteSolution, texteCompatibility] = sectionsOf<TexteImageBlock>(
    sections,
    "TexteImageLayout"
  );
  const comparatif = sectionsOf<TableauComparatifBlock>(
    sections,
    "TableauComparatifLayout"
  )[0];
  const situations = sectionsOf<TableauSituationsBlock>(
    sections,
    "TableauSituationsLayout"
  )[0];
  const etapes = sectionsOf<EtapesBlock>(sections, "EtapesLayout")[0];
  const chiffres = sectionsOf<ChiffresBlock>(sections, "ChiffresLayout")[0];
  const faqBlock = sectionsOf<FaqBlock>(sections, "FaqLayout")[0];
  const contenuSeo = sectionsOf<ContenuSeoBlock>(sections, "ContenuSeoLayout")[0];
  const ctas = sectionsOf<CtaBlock>(sections, "CtaLayout");
  const midCta = ctas.find((cta) => cta.variante === "mid") ?? ctas[0];
  const finalCta =
    ctas.find((cta) => cta.variante === "final") ??
    (ctas.length > 1 ? ctas[ctas.length - 1] : undefined);

  const heroImage = mapImage(hero?.image);

  // solution : l'export replie text + systemTitle dans `contenu`
  // (`<p>{text}</p>\n<h3>{systemTitle}</h3>`) et les couches dans `liste`.
  const solutionText = paragraphsOf(texteSolution?.contenu)[0];
  const solutionSystemTitle = stripHtml(
    texteSolution?.contenu?.match(/<h3>([\s\S]*?)<\/h3>/)?.[1]
  );

  // roi : l'entête (badge/titre/intro) est portée par le tableau_comparatif
  // s'il existe, sinon par le tableau_situations (intro + afterComparison y
  // sont alors repliés, séparés par une ligne vide).
  const roiEntete = comparatif ? comparatif.entete : situations?.entete;
  const [sitIntro, ...sitAfter] = (situations?.entete?.intro ?? "").split("\n\n");
  const roiIntro = comparatif ? str(comparatif.entete?.intro) : str(sitIntro);
  const roiAfter = comparatif
    ? str(comparatif.texteApres)
    : str(sitAfter.join("\n\n"));

  let comparison: TableBlock | undefined;
  if (
    comparatif &&
    (arr(comparatif.entetesColonnes).length || arr(comparatif.lignesComparatif).length)
  ) {
    comparison = {
      headers: arr(comparatif.entetesColonnes).map((col) => col.texte ?? ""),
      rows: arr(comparatif.lignesComparatif).map((row) =>
        arr(row.cellules).map((cell) => cell.texte ?? "")
      ),
    };
  }

  const situationRows = arr(situations?.lignesSituations);

  const faqItems = arr(faqBlock?.questions).length
    ? arr(faqBlock?.questions).map((item) => ({
        q: item.question ?? "",
        a: paragraphsOf(item.reponse).join("\n\n"),
      }))
    : undefined;

  const seoSections = arr(contenuSeo?.sections).length
    ? arr(contenuSeo?.sections).map((section) => ({
        title: section.titre ?? "",
        text: paragraphsOf(section.contenu).join("\n\n"),
      }))
    : undefined;

  const seoSources = arr(contenuSeo?.sources).length
    ? arr(contenuSeo?.sources).map((source) => ({
        label: source.label ?? "",
        href: source.url ?? "",
      }))
    : undefined;

  return {
    slug: slug || (fb?.slug ?? ""),
    path: fb?.path ?? `/toitures/${slug}`,
    title: str(toiture.seo?.titreSeo) ?? fb?.title ?? str(toiture.title) ?? "",
    description: str(toiture.seo?.metaDescription) ?? fb?.description ?? "",
    canonical:
      str(toiture.seo?.canonical) ??
      fb?.canonical ??
      `https://www.covalba.fr/toitures/${slug}`,
    eyebrow: str(hero?.eyebrow) ?? fb?.eyebrow ?? "",
    breadcrumb: str(toiture.title) ?? fb?.breadcrumb ?? "",
    h1: str(hero?.titre) ?? fb?.h1 ?? "",
    lead: str(hero?.lead) ?? fb?.lead ?? "",
    heroImage: heroImage?.sourceUrl ?? fb?.heroImage ?? "",
    heroImageAlt: str(heroImage?.altText) ?? fb?.heroImageAlt ?? "",
    heroStats: valueLabels(hero?.stats) ?? fb?.heroStats ?? [],
    reassurance: textes(barre?.items) ?? fb?.reassurance ?? [],
    problem: {
      badge: str(grilleProblem?.entete?.badge) ?? fb?.problem.badge ?? "",
      title: str(grilleProblem?.entete?.titre) ?? fb?.problem.title ?? "",
      cards: textCards(grilleProblem?.cards) ?? fb?.problem.cards ?? [],
      transition: str(grilleProblem?.transition) ?? fb?.problem.transition ?? "",
    },
    solution: {
      badge: str(texteSolution?.entete?.badge) ?? fb?.solution.badge ?? "",
      title: str(texteSolution?.entete?.titre) ?? fb?.solution.title ?? "",
      text: solutionText ?? fb?.solution.text ?? "",
      systemTitle: solutionSystemTitle ?? fb?.solution.systemTitle ?? "",
      system: textes(texteSolution?.liste) ?? fb?.solution.system ?? [],
      note: str(texteSolution?.note) ?? fb?.solution.note,
      cta: {
        label: str(texteSolution?.cta?.label) ?? fb?.solution.cta.label ?? "",
        href: str(texteSolution?.cta?.lien) ?? fb?.solution.cta.href ?? "",
      },
    },
    benefits: {
      badge: str(grilleBenefits?.entete?.badge) ?? fb?.benefits.badge ?? "",
      title: str(grilleBenefits?.entete?.titre) ?? fb?.benefits.title ?? "",
      featured: str(grilleBenefits?.entete?.intro) ?? fb?.benefits.featured ?? "",
      cards: textCards(grilleBenefits?.cards) ?? fb?.benefits.cards ?? [],
    },
    roi: {
      badge: str(roiEntete?.badge) ?? fb?.roi.badge ?? "",
      title: str(roiEntete?.titre) ?? fb?.roi.title ?? "",
      intro: roiIntro ?? fb?.roi.intro ?? "",
      comparison: comparison ?? fb?.roi.comparison,
      afterComparison: roiAfter ?? fb?.roi.afterComparison,
      situation: {
        headers: fb?.roi.situation.headers ?? SITUATION_HEADERS,
        rows: situationRows.length
          ? situationRows.map((row) => [
              row.situation ?? "",
              row.recommandation ?? "",
              row.justification ?? "",
            ])
          : fb?.roi.situation.rows ?? [],
      },
      cta: str(situations?.cta?.label) ?? fb?.roi.cta ?? "Demander un devis",
    },
    midCta: {
      title: str(midCta?.titre) ?? fb?.midCta.title ?? "",
      text: str(midCta?.texte) ?? fb?.midCta.text ?? "",
    },
    compatibility: {
      badge: str(texteCompatibility?.entete?.badge) ?? fb?.compatibility.badge ?? "",
      title: str(texteCompatibility?.entete?.titre) ?? fb?.compatibility.title ?? "",
      text:
        paragraphsOf(texteCompatibility?.contenu).join("\n\n") ||
        (fb?.compatibility.text ?? ""),
      image:
        mapImage(texteCompatibility?.image)?.sourceUrl ??
        fb?.compatibility.image ??
        "",
    },
    method: {
      badge: str(etapes?.entete?.badge) ?? fb?.method.badge ?? "",
      title: str(etapes?.entete?.titre) ?? fb?.method.title ?? "",
      steps: textCards(etapes?.etapes) ?? fb?.method.steps ?? [],
      reassurance: str(etapes?.reassurance) ?? fb?.method.reassurance ?? "",
    },
    proof: {
      badge: str(chiffres?.entete?.badge) ?? fb?.proof.badge ?? "",
      title: str(chiffres?.entete?.titre) ?? fb?.proof.title ?? "",
      figures: valueLabels(chiffres?.figures) ?? fb?.proof.figures ?? [],
    },
    faq: faqItems ?? fb?.faq ?? [],
    finalCta: {
      title: str(finalCta?.titre) ?? fb?.finalCta.title ?? "",
      text: str(finalCta?.texte) ?? fb?.finalCta.text ?? "",
      reassurances: textes(finalCta?.reassurances) ?? fb?.finalCta.reassurances ?? [],
    },
    seo: {
      intro:
        paragraphsOf(contenuSeo?.intro).join("\n\n") || (fb?.seo.intro ?? ""),
      sections: seoSections ?? fb?.seo.sections ?? [],
      sources: seoSources ?? fb?.seo.sources ?? [],
    },
    // Non représenté dans les sections WP (constante du contenu codé).
    productName: fb?.productName ?? "CovaTherm",
  };
}
