/* ============================================================
   « Ma méthode » — split screen, scroll-linked rotating dial
   ------------------------------------------------------------
   Left  : a dial. Steps sit on its circumference; the disc rotates
           with the scroll so the active step lands on the anchor
           (3 o'clock on desktop, 12 o'clock on mobile). The centre
           shows the active step's image behind a chrome ring.
   Right : title + description of the active step, synced with the
           rotation, CTA anchored at the bottom.

   Desktop : the section is 440vh tall, the inner block is sticky.
             Scroll progress → target rotation → damped (lerp) in a
             rAF loop that stops itself once settled. Only transform
             / opacity / filter are animated; the single layout read
             per scroll event is getBoundingClientRect on the wrap.
   Static  : (prefers-reduced-motion OR ≤ 1024px) no pinning; steps
             are clicked (or swiped on touch) and the disc rotates
             with a CSS transition (none under reduced motion).

   Steps live in METHOD_STEPS — markup is generated from it.
   ============================================================ */
const METHOD_STEPS = [
  { num: "01", title: "L'intention", desc: ["Moodboard, références, matières et couleurs.", "On cadre l'atmosphère avant de dessiner quoi que ce soit."], img: "uploads/process-1.webp" },
  { num: "02", title: "L'esquisse", desc: ["Croquis, recherches volumétriques, premières solutions d'aménagement.", "On teste, on écarte, on affine."], img: "uploads/process-2.webp" },
  { num: "03", title: "Le projet", desc: ["Plans, distribution, développement.", "La proposition se structure et devient constructible."], img: "uploads/process-3.webp" },
  { num: "04", title: "Le détail", desc: ["Plans et élévations texturés, choix des matériaux, calepinage.", "C'est ici que le projet gagne sa justesse."], img: "uploads/process-4.webp" },
  { num: "05", title: "L'image", desc: ["Modélisation 3D, mise en scène, rendus.", "Le projet devient lisible pour tous ceux qui le portent."], img: "uploads/process-5.webp" },
];

const METHOD_CONTACT = "mailto:studio@ines-madouri.fr";

function Method() {
  const { useEffect, useRef, useState } = React;

  const N = METHOD_STEPS.length;
  const STEP = 360 / N;             // angular distance between two steps
  const SWEEP = STEP * (N - 1);     // total rotation across the whole scroll
  const RADIUS = 49;                // % of the stage, where the dots sit

  const wrapRef = useRef(null);
  const stageRef = useRef(null);
  const discRef = useRef(null);
  const ringRef = useRef(null);     // chrome ring, rotates slower than the disc
  const sheenRef = useRef(null);    // moving highlight on the image
  const arcRef = useRef(null);      // progress arc (static svg)
  const labelRefs = useRef([]);

  const [active, setActive] = useState(0);
  const [reduced, setReduced] = useState(false);
  const [mobile, setMobile] = useState(false);
  const simple = reduced || mobile;

  // anchor: where the active step lands (deg, 0 = 3 o'clock, clockwise)
  const ANCHOR = mobile ? -90 : 0;
  const angleOf = (i) => ANCHOR + i * STEP;

  // ---- media queries ----------------------------------------------------
  useEffect(() => {
    const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
    const mqm = window.matchMedia("(max-width: 1024px)");
    const apply = () => { setReduced(mq.matches); setMobile(mqm.matches); };
    apply();
    mq.addEventListener("change", apply);
    mqm.addEventListener("change", apply);
    return () => { mq.removeEventListener("change", apply); mqm.removeEventListener("change", apply); };
  }, []);

  // ---- paint one rotation state (deg) onto the DOM ------------------------
  // Labels are children of the rotating disc: rotate(-rot) keeps them
  // upright, then they're pushed outward along the radial direction *in
  // screen space* so they always sit outside the ring.
  const paint = (rot, labelDist) => {
    if (discRef.current) discRef.current.style.transform = `rotate(${rot}deg)`;
    if (ringRef.current) ringRef.current.style.transform = `rotate(${rot * 0.35}deg)`;
    if (sheenRef.current) sheenRef.current.style.transform = `rotate(${-rot * 0.5}deg)`;
    for (let i = 0; i < N; i++) {
      const el = labelRefs.current[i];
      if (!el) continue;
      const s = (angleOf(i) + rot) * Math.PI / 180; // on-screen angle of this step
      el.style.transform = `rotate(${-rot}deg) translate(${Math.cos(s) * labelDist}px, ${Math.sin(s) * labelDist}px)`;
    }
  };
  const labelDistOf = (stage) => (stage ? stage.clientWidth * 0.12 : 40);

  // ---- desktop: scroll → damped rotation --------------------------------
  useEffect(() => {
    if (simple) return;
    const wrap = wrapRef.current, stage = stageRef.current;
    if (!wrap || !stage) return;

    let target = 0, current = 0, raf = 0, running = false;
    let labelDist = labelDistOf(stage);

    const apply = (p) => {
      paint(-p * SWEEP, labelDist);
      if (arcRef.current) arcRef.current.style.strokeDashoffset = String(100 * (1 - p));
      const i = Math.min(N - 1, Math.max(0, Math.round(p * (N - 1))));
      setActive((prev) => (prev === i ? prev : i));
    };
    const tick = () => {
      current += (target - current) * 0.085; // exponential damping
      if (Math.abs(target - current) < 0.0002) { current = target; apply(current); running = false; return; }
      apply(current);
      raf = requestAnimationFrame(tick);
    };
    const kick = () => { if (!running) { running = true; raf = requestAnimationFrame(tick); } };
    const readScroll = () => {
      const r = wrap.getBoundingClientRect();
      const span = r.height - window.innerHeight;
      target = span > 0 ? Math.min(1, Math.max(0, -r.top / span)) : 0;
      kick();
    };
    const onResize = () => { labelDist = labelDistOf(stage); readScroll(); };

    readScroll();
    current = target; apply(current);
    window.addEventListener("scroll", readScroll, { passive: true });
    window.addEventListener("resize", onResize);
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("scroll", readScroll);
      window.removeEventListener("resize", onResize);
    };
  }, [simple, N, SWEEP]);

  // ---- static / mobile: rotation follows the active index ---------------
  useEffect(() => {
    if (!simple) return;
    const run = () => paint(-active * STEP, labelDistOf(stageRef.current));
    run();
    if (arcRef.current) arcRef.current.style.strokeDashoffset = String(100 * (1 - active / (N - 1)));
    window.addEventListener("resize", run);
    return () => window.removeEventListener("resize", run);
  }, [simple, active, mobile, N, STEP]);

  // ---- navigation ---------------------------------------------------------
  const goTo = (i) => {
    const k = (i + N) % N;
    if (simple) { setActive(k); return; }
    const wrap = wrapRef.current;
    if (!wrap) return;
    const span = wrap.offsetHeight - window.innerHeight;
    window.scrollTo({ top: wrap.offsetTop + (k / (N - 1)) * span, behavior: "smooth" });
  };

  // swipe on the panel (static / mobile only)
  const touch = useRef(0);
  const onTouchStart = (e) => { touch.current = e.touches[0].clientX; };
  const onTouchEnd = (e) => {
    if (!simple) return;
    const dx = e.changedTouches[0].clientX - touch.current;
    if (Math.abs(dx) > 48) goTo(active + (dx < 0 ? 1 : -1));
  };

  const step = METHOD_STEPS[active];

  return (
    <section className={"method" + (simple ? " is-static" : "")} ref={wrapRef} id="methode">
      <div className="method__pin">
        <div className="method__inner">

          {/* ---------- left : the dial ---------- */}
          <div className="method__stage" ref={stageRef}>
            <div className="method__disc" ref={discRef}>
              <svg className="method__orbit" viewBox="0 0 100 100" aria-hidden="true">
                <circle className="method__orbit-line" cx="50" cy="50" r={RADIUS} />
                {/* dial ticks: 60 marks, every 12th longer */}
                {Array.from({ length: 60 }, (_, k) => {
                  const a = k * 6 * Math.PI / 180, long = k % 12 === 0;
                  const r1 = RADIUS - (long ? 4.2 : 2.2), r2 = RADIUS - 1;
                  return <line key={k} className={"method__tick" + (long ? " is-long" : "")}
                    x1={50 + r1 * Math.cos(a)} y1={50 + r1 * Math.sin(a)} x2={50 + r2 * Math.cos(a)} y2={50 + r2 * Math.sin(a)} />;
                })}
              </svg>

              {METHOD_STEPS.map((s, i) => {
                const a = angleOf(i) * Math.PI / 180;
                return (
                  <button
                    key={s.num}
                    type="button"
                    className={"mstep" + (i === active ? " is-active" : "")}
                    style={{ left: `${50 + RADIUS * Math.cos(a)}%`, top: `${50 + RADIUS * Math.sin(a)}%` }}
                    onClick={() => goTo(i)}
                    aria-label={`Étape ${s.num}, ${s.title}`}
                    aria-current={i === active ? "step" : undefined}
                  >
                    <span className="mstep__dot"></span>
                    <span className="mstep__label" ref={(el) => (labelRefs.current[i] = el)}>
                      <span className="mstep__num">{s.num}</span>
                      <span className="mstep__title">{s.title}</span>
                    </span>
                  </button>);
              })}
            </div>

            {/* static overlays: progress arc + anchor notch */}
            <svg className="method__arc" viewBox="0 0 100 100" aria-hidden="true" style={{ transform: `rotate(${ANCHOR}deg)` }}>
              <circle ref={arcRef} cx="50" cy="50" r={RADIUS} pathLength="100" style={{ strokeDasharray: 100, strokeDashoffset: 100 }} />
            </svg>
            <span className="method__notch" aria-hidden="true" style={{ transform: `translate(-50%,-50%) rotate(${ANCHOR}deg) translateX(${RADIUS - 3.6}cqw)` }}></span>

            {/* centre : chrome ring + crossfading images */}
            <div className="method__core">
              <span className="method__ring" ref={ringRef} aria-hidden="true"></span>
              <div className="method__frame">
                {METHOD_STEPS.map((s, i) =>
                  <img key={s.num} src={s.img} alt="" loading="lazy" decoding="async"
                    className={"method__img" + (i === active ? " is-active" : "")} />
                )}
                <span className="method__sheen" ref={sheenRef} aria-hidden="true"></span>
                <span className="method__chrome" aria-hidden="true"></span>
              </div>
            </div>
          </div>

          {/* ---------- right : synced content ---------- */}
          <div className="method__panel" onTouchStart={onTouchStart} onTouchEnd={onTouchEnd}>
            <div className="method__eyebrow">Ma méthode</div>

            <div className="method__copy" key={active}>
              <span className="method__bignum" aria-hidden="true">{step.num}</span>
              <span className="method__index">{step.num} <i>/ {String(N).padStart(2, "0")}</i></span>
              <h2 className="method__title"><span>{step.title}</span></h2>
              <p className="method__desc">
                {step.desc.map((line, k) => <span key={k}>{line}</span>)}
              </p>
            </div>

            <div className="method__foot">
              <ol className="method__nav" aria-label="Étapes">
                {METHOD_STEPS.map((s, i) =>
                  <li key={s.num}>
                    <button type="button" className={i === active ? "is-active" : ""} onClick={() => goTo(i)} aria-label={`Étape ${s.num}, ${s.title}`}>{s.num}</button>
                  </li>
                )}
              </ol>
              <p className="method__note">Une démarche itérative : j'interviens sur l'ensemble du parcours ou sur une seule de ses étapes.</p>
              <a className="method__cta" href={METHOD_CONTACT}>Nous contacter <span aria-hidden="true">→</span></a>
            </div>
          </div>

        </div>
      </div>
    </section>);
}

window.Method = Method;
