/* global React, ReactDOM, CaseHero, CaseFacts, CaseGallery, CaseQuote, CaseResults */
const { useEffect: useCSEffect, useRef: useCSRef } = React;

// Per-case overrides, keyed by ?id=… from the marketing site grid.
// Any case not listed falls back to the Baumanière reference template.
const CASE_DATA = {
  oustau: {
    cover: "../assets/photo-beaumaniere-1.jpg",
    cat: "Restaurant · 3★", year: "2025",
    title: "L'Oustau de Baumanière",
    client: "Maison Charial · Glenn Viel & J.A Charial",
    loc: "Les Baux-de-Provence",
    deliverables: "Photo · vidéo · motion · social",
    team: "S. Jullien (DA), 2 op. cadreurs",
    lede: "Six mois de reportage chez Glenn Viel pour traduire en images la table 3★, la maison et le savoir-faire au service.",
  },
  phebus: {
    cover: "../assets/photo-phebus-1.jpg",
    cat: "Hôtellerie & Spa · Restaurant · 1★", year: "2024",
    title: "Le Phébus & SPA — 40 ans",
    client: "Xavier Mathieu",
    loc: "Joucas · Vaucluse",
    deliverables: "Photo · vidéo · motion · social",
    team: "S. Jullien (DA), 2 op. cadreurs",
    lede: "Quarante ans de la maison Mathieu mis en images : dîners à 8 mains, spa et hôtellerie, capturés en reportage discret pendant la saison.",
  },
  sevin: {
    cover: "../assets/photo-sevin.jpg",
    cat: "Restaurant · 1★", year: "2024",
    title: "Restaurant SEVIN",
    client: "Guilhem Sevin",
    loc: "Avignon · Vaucluse",
    deliverables: "Photo · vidéo · social",
    team: "S. Jullien (DA), 1 op. cadreur",
    lede: "Vitrine digitale de l'étoilé Sevin : portraits, dressage, service. Une charte sobre, dense, fidèle à la cuisine de Guilhem.",
  },
  grizzly: {
    cover: "../assets/photo-portrait-1.jpg",
    cat: "Restaurant · 2★", year: "2025",
    title: "Le Grizzly",
    client: "Édouard Loubet",
    loc: "Provence",
    deliverables: "Photo · vidéo · motion",
    team: "S. Jullien (DA), 1 op. cadreur",
    lede: "Reportage long-cours dans la maison d'Édouard Loubet — la nature, le geste, la table.",
  },
  "altera-roma": {
    cover: "../assets/photo-altera-4.jpg",
    cat: "Hôtellerie · Boutique-hôtel", year: "2026",
    title: "Altera Roma",
    client: "Altera Roma",
    loc: "Rome · Italie",
    deliverables: "Photo · vidéo · social",
    team: "S. Jullien (DA), 1 op. cadreur",
    lede: "Un boutique-hôtel romain à la palette terracotta, jungle et terrazzo — capter en images l'identité chromatique forte et l'esprit d'évasion des chambres.",
    photos: [
      { src: "../assets/photo-altera-4.jpg", cap: "Chambre · fresque jungle & terracotta" },
      { src: "../assets/photo-altera-1.jpg", cap: "Chambre · fresque palmiers" },
      { src: "../assets/photo-altera-2.jpg", cap: "Salle de bain · terrazzo & terracotta" },
      { src: "../assets/photo-altera-3.jpg", cap: "Vasque · terrazzo signature" },
      { src: "../assets/photo-altera-5.jpg", cap: "Détail · chapeau & robinetterie noire" },
      { src: "../assets/photo-altera-6.jpg", cap: "Suite mansardée · pierre & poutres" },
      { src: "../assets/photo-altera-7.jpg", cap: "Tête de lit · pierre apparente" },
    ],
  },
};

const DEFAULT_ID = "oustau";
function pickId() {
  try {
    const p = new URLSearchParams(window.location.search).get("id");
    if (!p) return DEFAULT_ID;
    return p;
  } catch { return DEFAULT_ID; }
}

// Merge content.json caseStudyPages over the legacy CASE_DATA so existing pages
// keep working until they're filled in via the back-office.
function resolveData(id) {
  const fromJson = (window.SITE_CONTENT && window.SITE_CONTENT.caseStudyPages && window.SITE_CONTENT.caseStudyPages[id]) || null;
  const fromLegacy = CASE_DATA[id] || null;
  if (!fromJson && !fromLegacy) return null;
  // Json takes precedence; legacy fills gaps.
  return Object.assign({}, fromLegacy || {}, fromJson || {});
}

const PARAM_ID = pickId();
// DATA is resolved inside App() — by then the async loader has populated SITE_CONTENT.

const DEFAULT_PHOTOS = [
  { src: "assets/photo-beaumaniere-1.jpg", cap: "Cuisine · service du soir" },
  { src: "assets/photo-beaumaniere-2.jpg", cap: "Plat signature" },
  { src: "assets/photo-beaumaniere-3.jpg", cap: "Brigade · mise en place" },
  { src: "assets/photo-portrait-1.jpg", cap: "Portrait · chef" },
  { src: "assets/photo-aerial-DJI.jpg", cap: "Aérien DJI · domaine" },
  { src: "assets/photo-phebus-2.jpg", cap: "Détail · dressage" },
];

const DEFAULT_STATS = [
  { n: "516 188", label: "vues organiques" },
  { n: "18 907", label: "j'aime" },
  { n: "703", label: "enregistrements" },
  { n: "341 550", label: "comptes touchés" },
];

// Ambient desert dunes — on a case-study page we keep the dunes visible from
// just below the hero to the bottom of the page. Fade-out near the CTA.
function CSDesertDunesBackground() {
  useCSEffect(() => {
    let raf = 0;
    let smoothWind = 0;
    let smoothOp = 0;
    const update = () => {
      raf = 0;
      const max = Math.max(1, document.documentElement.scrollHeight - window.innerHeight);
      const p = Math.min(1, Math.max(0, window.scrollY / max));
      smoothWind = smoothWind + (p - smoothWind) * 0.35;
      // Visible band: from just past the hero (~80vh) until the CTA section
      const vh = window.innerHeight;
      const cta = document.querySelector('.cs-cta');
      const heroEnd = vh * 0.7;
      let target = 0;
      if (window.scrollY > heroEnd) {
        target = Math.min(1, (window.scrollY - heroEnd) / (vh * 0.45));
      }
      if (cta) {
        const ctaTop = cta.getBoundingClientRect().top;
        if (ctaTop < vh * 0.4) target *= Math.max(0, ctaTop / (vh * 0.4));
      }
      smoothOp = smoothOp + (target - smoothOp) * 0.22;
      const gust = 0.4 + Math.pow(smoothWind, 0.8) * 2.4;
      const root = document.documentElement;
      root.style.setProperty('--wind', smoothWind.toFixed(3));
      root.style.setProperty('--gust', gust.toFixed(2));
      root.style.setProperty('--dunes-opacity', smoothOp.toFixed(3));
      if (Math.abs(target - smoothOp) > 0.001) raf = requestAnimationFrame(update);
    };
    const onScroll = () => { if (!raf) raf = requestAnimationFrame(update); };
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    update();
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
      if (raf) cancelAnimationFrame(raf);
    };
  }, []);
  return (
    <div className="fn-bg-dunes" aria-hidden="true">
      <div className="fn-bg-dunes__far"/>
      <div className="fn-bg-dunes__near"/>
      <div className="fn-bg-dunes__streaks"/>
      <div className="fn-bg-dunes__sand"/>
      <div className="fn-bg-dunes__shade"/>
    </div>
  );
}

function CSFennecCursor() {
  const ref = useCSRef(null);
  useCSEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (window.matchMedia("(pointer: coarse)").matches) return;
    document.documentElement.classList.add("cs-has-cursor");
    let x = -100, y = -100, tx = -100, ty = -100;
    const onMove = (e) => { tx = e.clientX; ty = e.clientY; };
    const onDown = () => el.classList.add("is-down");
    const onUp = () => el.classList.remove("is-down");
    const onOver = (e) => {
      const t = e.target;
      const link = t.closest && t.closest("a, button, [role=button], input, textarea, label");
      el.classList.toggle("is-link", !!link);
    };
    const tick = () => {
      x += (tx - x) * 0.4; y += (ty - y) * 0.4;
      el.style.transform = `translate(${x - 1}px, ${y - 1}px)`;
      raf = requestAnimationFrame(tick);
    };
    let raf = requestAnimationFrame(tick);
    window.addEventListener("mousemove", onMove);
    window.addEventListener("mousedown", onDown);
    window.addEventListener("mouseup", onUp);
    window.addEventListener("mouseover", onOver);
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mousedown", onDown);
      window.removeEventListener("mouseup", onUp);
      window.removeEventListener("mouseover", onOver);
      document.documentElement.classList.remove("cs-has-cursor");
    };
  }, []);
  return (
    <div className="cs-cursor" ref={ref}>
      <img src="../assets/arrow-fennec.png" alt=""/>
    </div>
  );
}

function useCSReveal() {
  useCSEffect(() => {
    const obs = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) { e.target.classList.add("is-in"); obs.unobserve(e.target); }
      });
    }, { threshold: 0.12, rootMargin: "0px 0px -8% 0px" });
    document.querySelectorAll(".cs-reveal").forEach((el) => obs.observe(el));
    return () => obs.disconnect();
  });
}

function App() {
  useCSReveal();
  const data = resolveData(PARAM_ID) || resolveData(DEFAULT_ID);
  if (!data) return <div style={{padding: 48, color: "var(--fg-1)"}}>Case study introuvable.</div>;

  const blocks = Array.isArray(data.blocks) ? data.blocks : null;

  return (
    <div className="cs-app fennec-page">
      <CSDesertDunesBackground/>
      <CSFennecCursor/>
      <nav className="cs-nav">
        <a href="../index.html#realisations" className="back">← Réalisations</a>
        <img src="../assets/fennec-logo-master.jpg" alt="FENNEC" style={{height: 32, width: 32, objectFit: "cover"}}/>
        <a href="#contact" className="back">Travaillons ensemble →</a>
      </nav>

      <CaseHero data={data}/>

      <div className="cs-body cs-reveal">
        <CaseFacts data={data}/>
        {blocks
          ? <CaseBlocks blocks={blocks}/>
          : <LegacyProse photos={(data.photos || DEFAULT_PHOTOS).map(p => ({...p, src: caseAsset(p.src)}))} stats={DEFAULT_STATS}/>}
      </div>

      {!blocks && (
        <>
          <div className="cs-reveal"><CaseGallery photos={(data.photos || DEFAULT_PHOTOS).map(p => ({...p, src: caseAsset(p.src)}))}/></div>
          <div className="cs-reveal"><CaseResults stats={DEFAULT_STATS}/></div>
        </>
      )}

      <section className="cs-cta cs-reveal" id="contact">
        <h2>Travaillons ensemble.</h2>
        <a
          href={(window.SITE_CONTENT && window.SITE_CONTENT.contact && window.SITE_CONTENT.contact.calendly) || "../index.html#contact"}
          target={(window.SITE_CONTENT && window.SITE_CONTENT.contact && window.SITE_CONTENT.contact.calendly) ? "_blank" : "_self"}
          rel="noopener"
        >
          Prendre rendez-vous
          <img src="../assets/arrow-fennec.png" alt=""/>
        </a>
      </section>
    </div>
  );
}

// Inline prose fallback for case studies without an explicit blocks array.
function LegacyProse() {
  return (
    <div className="cs-prose">
      <h2>Le brief</h2>
      <p>La maison souhaitait une vitrine digitale à la hauteur de sa table et de son savoir-faire. Il fallait passer d'une présence sociale ponctuelle à un récit construit, régulier, et fidèle au rythme du service.</p>
      <h2>L'approche</h2>
      <p>Reportage discret en service, exigeant sur l'image. Une charte photo cohérente — golden hour, deep blacks, gestes du métier — déclinée sur reels, photos et motion. Calendrier éditorial mensuel, validation simplifiée, livrables prêts à publier.</p>
      <h2>Les livrables</h2>
      <p>Photos retouchées, reels signature, motion pour les temps forts. Distribution Instagram + TikTok avec un plan de promotion sur les fenêtres clés.</p>
    </div>
  );
}

// Wait for SITE_CONTENT before rendering so DATA resolves against the latest JSON.
(async () => {
  if (window.SITE_CONTENT_READY) { try { await window.SITE_CONTENT_READY; } catch (_) {} }
  ReactDOM.createRoot(document.getElementById("root")).render(<App/>);
})();
