// Mapper inverse : reconstruit l'objet de config du SectorPageTemplate
// (Tertiaire / Collectivités / Agricole / ERP) depuis les sections WordPress
// du post `industrie`. Chaque champ est mappé avec fallback sur la config
// codée existante : valeur WP absente/vide => valeur du fallback (rendu
// identique au site pré-CMS).
//
// Matching des rows : par layout (suffixe `...XxxLayout` du __typename),
// dans l'ordre du flexible, avec les mêmes conventions que les seeds
// (wordpress/seed/content/industrie--{tertiaire,collectivites,agricole,erp}.json) :
//   hero → logos (bandeau hero) → grille_cards (problème) → texte_image (solution)
//   → tableau_comparatif (durabilité) → texte_image+cta (section spéciale)
//   → chiffres clair (bénéfices) → grille_cards sans badge (cards bénéfices)
//   → grille_cards (applications) → etapes (méthode) → chiffres sombre (preuves)
//   → references_grille → logos (preuves) → faq → cta (final) → contenu_seo.
// Quand un champ `origine` est posé sur une row, il sert d'indice prioritaire
// (valeur attendue : "SectorPageTemplate"), mais le matching reste par layout.

import type { SectorPageConfig } from "@/components/industries/SectorPageTemplate";
import { arr, mapImage, parseAccent } from "./mappers";
import type {
  ChiffresBlock,
  CtaBlock,
  ContenuSeoBlock,
  EtapesBlock,
  FaqBlock,
  GrilleCardsBlock,
  HeroBlock,
  LogosBlock,
  TableauComparatifBlock,
  TexteImageBlock,
  WpSection,
} from "./types";

// ---------------------------------------------------------------------------
// Petits utilitaires texte
// ---------------------------------------------------------------------------

/**
 * Décode les entités HTML courantes + la texturisation WordPress
 * (wptexturize transforme `'` en `&rsquo;`, `"` en `&ldquo;`/`&rdquo;`, etc.).
 */
const decode = (s: string): string =>
  s
    .replace(/&nbsp;|&#160;/g, " ")
    .replace(/&lt;/g, "<")
    .replace(/&gt;/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, "&");

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

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

/** Titre sans la convention `*accent*`. */
const plain = (titre?: string | null): string | undefined =>
  titre ? val(titre.replace(/\*/g, "")) : undefined;

/**
 * "Tête *accentuée.*" → { head: "Tête", muted: "accentuée." }.
 * Le template rend `{title}<br/><span muted>{mutedTitle}</span>`.
 */
const splitAccent = (
  titre?: string | null,
): { head?: string; muted?: string } => {
  if (!titre) return {};
  if (!titre.includes("*")) return { head: plain(titre) };
  const segments = parseAccent(titre);
  const head = segments
    .filter((s) => !s.accent)
    .map((s) => s.text)
    .join("")
    .trim();
  const muted = segments.find((s) => s.accent)?.text.trim();
  return { head: head || undefined, muted: muted || undefined };
};

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

/** "<strong>valeur</strong> — label" (ou sans tiret) → { value, label }. */
const strongItem = (li: string): { value?: string; label?: string } => {
  const m = li.match(/<strong>([\s\S]*?)<\/strong>\s*(?:—\s*)?([\s\S]*)/);
  if (!m) return { label: text(li) };
  return { value: text(m[1]), label: text(m[2]) };
};

/** Paires <h3>titre</h3> suivi d'un <ul> (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: liItems(m[2]).map((li) => text(li) ?? "").filter(Boolean),
      }))
    : [];

// ---------------------------------------------------------------------------
// Matching des sections par layout
// ---------------------------------------------------------------------------

const isLayout = (section: WpSection, suffix: string): boolean =>
  typeof section.__typename === "string" && section.__typename.endsWith(suffix);

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

export function toSectorConfig(
  sections: WpSection[] | null | undefined,
  fallback: SectorPageConfig,
): SectorPageConfig {
  // Copie profonde "manuelle" du fallback (les icônes Lucide sont des
  // fonctions : pas de structuredClone possible).
  const config: SectorPageConfig = {
    ...fallback,
    reassurance: fallback.reassurance ? [...fallback.reassurance] : undefined,
    hero: {
      ...fallback.hero,
      stats: fallback.hero.stats.map((s) => ({ ...s })),
      formFields: {
        ...fallback.hero.formFields,
        typeOptions: [...fallback.hero.formFields.typeOptions],
        climateOptions: [...fallback.hero.formFields.climateOptions],
      },
    },
    logos: { ...fallback.logos, items: fallback.logos.items.map((l) => ({ ...l })) },
    problem: { ...fallback.problem, cards: fallback.problem.cards.map((c) => ({ ...c })) },
    solution: { ...fallback.solution, points: [...fallback.solution.points] },
    durability: { ...fallback.durability },
    special:
      fallback.special.kind === "decret"
        ? {
            ...fallback.special,
            milestones: fallback.special.milestones.map((m) => ({ ...m })),
            points: [...fallback.special.points],
          }
        : {
            ...fallback.special,
            points: fallback.special.points.map((p) => ({ ...p })),
          },
    benefits: { ...fallback.benefits, cards: fallback.benefits.cards.map((c) => ({ ...c })) },
    applications: {
      ...fallback.applications,
      cards: fallback.applications.cards.map((c) => ({ ...c })),
    },
    method: { ...fallback.method, steps: fallback.method.steps.map((s) => ({ ...s })) },
    proof: {
      ...fallback.proof,
      figures: fallback.proof.figures.map((f) => ({ ...f })),
      cases: fallback.proof.cases?.map((c) => ({ ...c })),
      logos: fallback.proof.logos.map((l) => ({ ...l })),
    },
    faq: { ...fallback.faq, items: fallback.faq.items.map((i) => ({ ...i })) },
    finalCta: { ...fallback.finalCta, reassurances: [...fallback.finalCta.reassurances] },
    seo: {
      ...fallback.seo,
      sections: fallback.seo.sections.map((s) => ({ ...s })),
      sources: [...fallback.seo.sources],
    },
  };

  if (!sections?.length) return config;

  // -- Collecte par layout, dans l'ordre du flexible ------------------------
  const hero = sections.find((s) => isLayout(s, "HeroLayout")) as HeroBlock | undefined;
  const logosBlocks = sections.filter((s) => isLayout(s, "LogosLayout")) as LogosBlock[];
  const grilles = sections.filter((s) => isLayout(s, "GrilleCardsLayout")) as GrilleCardsBlock[];
  const texteImages = sections.filter((s) => isLayout(s, "TexteImageLayout")) as TexteImageBlock[];
  const chiffres = sections.filter((s) => isLayout(s, "ChiffresLayout")) as ChiffresBlock[];
  const tableaux = sections.filter((s) =>
    isLayout(s, "TableauComparatifLayout"),
  ) as TableauComparatifBlock[];
  const etapes = sections.find((s) => isLayout(s, "EtapesLayout")) as EtapesBlock | undefined;
  const faq = sections.find((s) => isLayout(s, "FaqLayout")) as FaqBlock | undefined;
  const cta = sections.find((s) => isLayout(s, "CtaLayout")) as CtaBlock | undefined;
  const seo = sections.find((s) => isLayout(s, "ContenuSeoLayout")) as
    | ContenuSeoBlock
    | undefined;

  // grille_cards : problème (1re avec badge), cards bénéfices (1re sans
  // badge), applications (2e avec badge).
  const badged = grilles.filter((g) => val(g.entete?.badge));
  const problemBlock = badged[0];
  const applicationsBlock = badged[1];
  const benefitsCardsBlock = grilles.find((g) => !val(g.entete?.badge));

  // texte_image : solution (sans CTA), section spéciale (avec CTA).
  const solutionBlock = texteImages.find((t) => !t.cta?.label);
  const specialBlock = texteImages.find((t) => Boolean(t.cta?.label));

  // chiffres : bénéfices (clair), preuves (sombre).
  const benefitsMetricBlock = chiffres.find((c) => c.theme !== "sombre");
  const proofBlock = chiffres.find((c) => c.theme === "sombre");

  const durabilityBlock = tableaux[0];

  // -- HERO ------------------------------------------------------------------
  if (hero) {
    const img = mapImage(hero.image);
    config.hero.eyebrow = val(hero.eyebrow) ?? config.hero.eyebrow;
    config.hero.h1 = plain(hero.titre) ?? config.hero.h1;
    config.hero.sub = val(hero.lead) ?? config.hero.sub;
    if (img) {
      config.hero.image = img.sourceUrl;
      config.hero.imageAlt = img.altText || config.hero.imageAlt;
    }
    const stats = arr(hero.stats);
    if (stats.length) {
      config.hero.stats = config.hero.stats.map((s, i) => ({
        ...s,
        value: val(stats[i]?.value) ?? s.value,
        label: val(stats[i]?.label) ?? s.label,
      }));
    }
    config.hero.formFields.cta = val(hero.ctaPrimaire?.label) ?? config.hero.formFields.cta;
  }

  // -- LOGOS (bandeau hero + bloc preuves) ------------------------------------
  // Image WP manquante (ex. SVG refusé à l'upload par WordPress) → on comble
  // avec l'URL du logo codé portant le même nom (deep-merge champ à champ).
  const mapLogos = (
    block: LogosBlock | undefined,
    fallbackItems: { name: string; url?: string }[],
  ) => {
    const items = arr(block?.logosCustom)
      .map((l) => {
        const img = mapImage(l.image);
        const name = l.nom ?? img?.altText ?? "";
        const fb = fallbackItems.find((f) => f.name === name);
        return { name, url: img?.sourceUrl ?? fb?.url };
      })
      .filter((l) => l.name);
    return items.length ? items : undefined;
  };
  const heroLogos = mapLogos(logosBlocks[0], fallback.logos.items);
  if (heroLogos) config.logos.items = heroLogos;
  config.logos.title = val(logosBlocks[0]?.entete?.titre) ?? config.logos.title;
  const proofLogos = mapLogos(logosBlocks[1], fallback.proof.logos);
  if (proofLogos) config.proof.logos = proofLogos;

  // -- PROBLÈME ----------------------------------------------------------------
  if (problemBlock) {
    const { head, muted } = splitAccent(problemBlock.entete?.titre);
    config.problem.eyebrow = val(problemBlock.entete?.badge) ?? config.problem.eyebrow;
    config.problem.title = head ?? config.problem.title;
    config.problem.mutedTitle = muted ?? config.problem.mutedTitle;
    config.problem.intro = val(problemBlock.entete?.intro) ?? config.problem.intro;
    config.problem.transition = val(problemBlock.transition) ?? config.problem.transition;
    const cards = arr(problemBlock.cards);
    if (cards.length) {
      config.problem.cards = config.problem.cards.map((card, i) => {
        const c = cards[i];
        if (!c) return card;
        const img = mapImage(c.image);
        return {
          ...card,
          title: val(c.titre) ?? card.title,
          desc: val(c.texte) ?? card.desc,
          image: img?.sourceUrl ?? card.image,
          alt: img?.altText || card.alt,
        };
      });
    }
  }

  // -- SOLUTION ------------------------------------------------------------------
  if (solutionBlock) {
    const { head, muted } = splitAccent(solutionBlock.entete?.titre);
    config.solution.eyebrow = val(solutionBlock.entete?.badge) ?? config.solution.eyebrow;
    config.solution.title = head ?? config.solution.title;
    config.solution.mutedTitle = muted ?? config.solution.mutedTitle;
    config.solution.intro = val(solutionBlock.entete?.intro) ?? config.solution.intro;
    const points = arr(solutionBlock.liste)
      .map((l) => val(l.texte))
      .filter((t): t is string => Boolean(t));
    if (points.length) config.solution.points = points;
    config.solution.transition = val(solutionBlock.note) ?? config.solution.transition;
    const img = mapImage(solutionBlock.image);
    if (img) config.solution.afterImage = img.sourceUrl;
  }

  // -- DURABILITÉ ------------------------------------------------------------------
  if (durabilityBlock) {
    const { head, muted } = splitAccent(durabilityBlock.entete?.titre);
    config.durability.title = head ?? config.durability.title;
    if (muted) config.durability.mutedTitle = muted;
    config.durability.intro = val(durabilityBlock.entete?.intro) ?? config.durability.intro;
    config.durability.note = val(durabilityBlock.texteApres) ?? config.durability.note;
    const firstRow = arr(durabilityBlock.lignesComparatif)[0];
    const covalbaLabel = val(arr(firstRow?.cellules)[2]?.texte);
    config.durability.covalbaLabel = covalbaLabel ?? config.durability.covalbaLabel;
  }

  // -- SECTION SPÉCIALE (décret / financement) ---------------------------------------
  if (specialBlock) {
    config.special.eyebrow = val(specialBlock.entete?.badge) ?? config.special.eyebrow;
    config.special.title = plain(specialBlock.entete?.titre) ?? config.special.title;
    config.special.intro = val(specialBlock.entete?.intro) ?? config.special.intro;
    config.special.note = val(specialBlock.note) ?? config.special.note;
    config.special.cta = val(specialBlock.cta?.label) ?? config.special.cta;
    const items = liItems(specialBlock.contenu).map(strongItem);
    if (config.special.kind === "decret") {
      const fbMilestones = fallback.special.kind === "decret" ? fallback.special.milestones : [];
      if (items.length) {
        config.special.milestones = items.map((it, i) => ({
          value: it.value ?? fbMilestones[i]?.value ?? "",
          label: it.label ?? fbMilestones[i]?.label ?? "",
        }));
      }
      const points = arr(specialBlock.liste)
        .map((l) => val(l.texte))
        .filter((t): t is string => Boolean(t));
      if (points.length) config.special.points = points;
    } else if (items.length) {
      const fbPoints = fallback.special.kind === "funding" ? fallback.special.points : [];
      config.special.points = items.map((it, i) => ({
        value: it.value ?? fbPoints[i]?.value ?? "",
        label: it.label ?? fbPoints[i]?.label ?? "",
      }));
    }
  }

  // -- BÉNÉFICES (métrique + cartes) ---------------------------------------------------
  if (benefitsMetricBlock) {
    config.benefits.eyebrow = val(benefitsMetricBlock.entete?.badge) ?? config.benefits.eyebrow;
    config.benefits.title = plain(benefitsMetricBlock.entete?.titre) ?? config.benefits.title;
    config.benefits.desc = val(benefitsMetricBlock.entete?.intro) ?? config.benefits.desc;
    const figure = arr(benefitsMetricBlock.figures)[0];
    if (figure) {
      config.benefits.metric = val(figure.value) ?? config.benefits.metric;
      config.benefits.metricLabel = val(figure.label) ?? config.benefits.metricLabel;
    }
  }
  if (benefitsCardsBlock) {
    config.benefits.transition = val(benefitsCardsBlock.transition) ?? config.benefits.transition;
    const cards = arr(benefitsCardsBlock.cards);
    if (cards.length) {
      config.benefits.cards = config.benefits.cards.map((card, i) => {
        const c = cards[i];
        if (!c) return card;
        return {
          ...card,
          title: val(c.titre) ?? card.title,
          desc: val(c.texte) ?? card.desc,
        };
      });
    }
  }

  // -- APPLICATIONS --------------------------------------------------------------------
  if (applicationsBlock) {
    config.applications.eyebrow =
      val(applicationsBlock.entete?.badge) ?? config.applications.eyebrow;
    config.applications.title = plain(applicationsBlock.entete?.titre) ?? config.applications.title;
    config.applications.intro = val(applicationsBlock.entete?.intro) ?? config.applications.intro;
    config.applications.transition =
      val(applicationsBlock.transition) ?? config.applications.transition;
    const cards = arr(applicationsBlock.cards);
    if (cards.length) {
      config.applications.cards = config.applications.cards.map((card, i) => {
        const c = cards[i];
        if (!c) return card;
        const img = mapImage(c.image);
        return {
          ...card,
          title: val(c.titre) ?? card.title,
          desc: val(c.texte) ?? card.desc,
          image: img?.sourceUrl ?? card.image,
        };
      });
    }
  }

  // -- MÉTHODE ----------------------------------------------------------------------------
  if (etapes) {
    const { head, muted } = splitAccent(etapes.entete?.titre);
    config.method.eyebrow = val(etapes.entete?.badge) ?? config.method.eyebrow;
    config.method.title = head ?? config.method.title;
    config.method.mutedTitle = muted ?? config.method.mutedTitle;
    config.method.intro = val(etapes.entete?.intro) ?? config.method.intro;
    const steps = arr(etapes.etapes);
    if (steps.length) {
      config.method.steps = config.method.steps.map((step, i) => ({
        ...step,
        title: val(steps[i]?.titre) ?? step.title,
        text: val(steps[i]?.texte) ?? step.text,
      }));
    }
    config.method.duration = val(etapes.reassurance) ?? config.method.duration;
  }

  // -- PREUVES ----------------------------------------------------------------------------
  if (proofBlock) {
    const { head, muted } = splitAccent(proofBlock.entete?.titre);
    config.proof.eyebrow = val(proofBlock.entete?.badge) ?? config.proof.eyebrow;
    config.proof.title = head ?? config.proof.title;
    config.proof.mutedTitle = muted ?? config.proof.mutedTitle;
    config.proof.intro = val(proofBlock.entete?.intro) ?? config.proof.intro;
    const figures = arr(proofBlock.figures);
    if (figures.length) {
      config.proof.figures = config.proof.figures.map((f, i) => ({
        ...f,
        value: val(figures[i]?.value) ?? f.value,
        label: val(figures[i]?.label) ?? f.label,
      }));
    }
  }

  // -- FAQ ----------------------------------------------------------------------------------
  if (faq) {
    config.faq.intro = val(faq.entete?.intro) ?? config.faq.intro;
    const items = arr(faq.questions)
      .map((q) => ({ q: val(q.question) ?? "", a: text(q.reponse) ?? "" }))
      .filter((i) => i.q && i.a);
    if (items.length) config.faq.items = items;
  }

  // -- CTA FINAL ------------------------------------------------------------------------------
  if (cta) {
    config.finalCta.title = plain(cta.titre) ?? config.finalCta.title;
    config.finalCta.desc = val(cta.texte) ?? config.finalCta.desc;
    const reassurances = arr(cta.reassurances)
      .map((r) => val(r.texte))
      .filter((t): t is string => Boolean(t));
    if (reassurances.length) config.finalCta.reassurances = reassurances;
  }

  // -- CONTENU SEO ------------------------------------------------------------------------------
  if (seo) {
    const h2 = seo.intro?.match(/<h2[^>]*>([\s\S]*?)<\/h2>/);
    const p = seo.intro?.match(/<p[^>]*>([\s\S]*?)<\/p>/);
    config.seo.title = (h2 ? text(h2[1]) : undefined) ?? config.seo.title;
    config.seo.intro = (p ? text(p[1]) : undefined) ?? config.seo.intro;
    const secs = arr(seo.sections)
      .map((s) => ({ title: val(s.titre) ?? "", body: text(s.contenu) ?? "" }))
      .filter((s) => s.title && s.body);
    if (secs.length) config.seo.sections = secs;
    const sources = arr(seo.sources)
      .map((s) => val(s.label))
      .filter((t): t is string => Boolean(t));
    if (sources.length) config.seo.sources = sources;
  }

  return config;
}

export default toSectorConfig;
