/* global React */

// ─── Photo placeholder ───────────────────────────────────────
function PhotoSlot({ label, aspect, className = "", style = {}, zoomable = false, children, hideLabel = false }) {
  const cls = `bm-photo${zoomable ? " is-zoomable" : ""}${hideLabel ? " bm-photo-with-meta" : ""} ${className}`.trim();
  const s = { ...style };
  if (aspect) s.aspectRatio = aspect;
  return (
    <div className={cls} style={s}>
      {children}
      {!hideLabel && <span className="bm-photo-label">[ {label} ]</span>}
    </div>
  );
}

// ─── Editorial photo (image + meta caption strip) ────────────
function EditorialPhoto({ aspect = "4 / 5", frame, tags = [], coords = "CAMBRIDGE · UK", style = {}, zoomable = true, label = "" }) {
  return (
    <figure style={{ margin: 0, ...style }}>
      <PhotoSlot
        hideLabel
        label={label}
        zoomable={zoomable}
        aspect={aspect}
        style={{ width: "100%" }}
      />
      <figcaption className="bm-photo-meta">
        <span className="left">
          {frame && <span className="frame">FR. {frame}</span>}
          {tags.map((t, i) => <span key={i}>{t}</span>)}
        </span>
        <span>{coords}</span>
      </figcaption>
    </figure>
  );
}

// ─── Marquee strip ───────────────────────────────────────────
function Marquee({ items = [], motion = true }) {
  const content = items.map((t, i) => (
    <React.Fragment key={i}>
      <span>{t}</span>
      <span className="dot">●</span>
    </React.Fragment>
  ));
  if (!motion) {
    return (
      <div className="bm-marquee">
        <div className="bm-marquee-track" style={{ animation: "none" }}>
          <span>{content}</span>
        </div>
      </div>
    );
  }
  return (
    <div className="bm-marquee">
      <div className="bm-marquee-track">
        <span>{content}</span>
        <span aria-hidden="true">{content}</span>
      </div>
    </div>
  );
}

// ─── Scroll fade-up wrapper ───────────────────────────────────
function FadeIn({ children, delay = 0, as = "div", className = "", ...rest }) {
  const ref = React.useRef(null);
  const Tag = as;
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          setTimeout(() => el.classList.add("is-in"), delay);
          io.unobserve(el);
        }
      });
    }, { threshold: 0.12, rootMargin: "0px 0px -60px 0px" });
    io.observe(el);
    return () => io.disconnect();
  }, [delay]);
  return <Tag ref={ref} className={`bm-fade ${className}`.trim()} {...rest}>{children}</Tag>;
}

// ─── Logo wordmark ────────────────────────────────────────────
function Logo({ onClick, light = false }) {
  return (
    <a
      className="bm-logo"
      onClick={(e) => { e.preventDefault(); onClick && onClick(); }}
      href="#/"
      style={light ? { color: "var(--bg-primary)" } : undefined}
    >
      BoMovement<span className="dot" />
    </a>
  );
}

// ─── Nav ──────────────────────────────────────────────────────
function Nav({ route, navigate, isMobile }) {
  const [scrolled, setScrolled] = React.useState(false);
  const [open, setOpen] = React.useState(false);

  React.useEffect(() => {
    const root = document.querySelector(".bm-scrollroot") || window;
    const onScroll = () => {
      const y = root === window ? window.scrollY : root.scrollTop;
      setScrolled(y > 8);
    };
    root.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => root.removeEventListener("scroll", onScroll);
  }, []);

  const links = [
    { id: "sessions", label: "FRC Sessions" },
    { id: "masterclass", label: "Masterclass" },
    { id: "about", label: "About" },
  ];

  const go = (id) => { navigate(id); setOpen(false); };

  return (
    <>
      <nav className={`bm-nav ${scrolled ? "is-scrolled" : ""}`}>
        <div className="bm-nav-inner">
          <div style={{ display: "flex", alignItems: "center", gap: 16 }}>
            <Logo onClick={() => go("home")} />
          </div>

          <div className="bm-nav-links bm-desktop-only">
            {links.map(l => (
              <a
                key={l.id}
                className={`bm-nav-link ${route === l.id ? "is-active" : ""}`}
                onClick={(e) => { e.preventDefault(); go(l.id); }}
                href={`#/${l.id}`}
              >{l.label}</a>
            ))}
            <a
              className="bm-btn bm-btn-primary"
              onClick={(e) => { e.preventDefault(); go("contact"); }}
              href="#/contact"
              style={{ height: 40, padding: "0 18px" }}
            >Enquire</a>
          </div>

          <button className="bm-burger" onClick={() => setOpen(o => !o)} aria-label="Menu">
            <span className="bm-burger-line" />
          </button>
        </div>
      </nav>

      <div className={`bm-mobile-menu ${open ? "is-open" : ""}`}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
          <Logo onClick={() => go("home")} />
          <button className="bm-burger" onClick={() => setOpen(false)} aria-label="Close" style={{ fontSize: 24 }}>
            <span style={{ fontSize: 28, lineHeight: 1 }}>×</span>
          </button>
        </div>
        <div className="bm-mobile-menu-list">
          <a onClick={() => go("home")} href="#/">Home</a>
          <a onClick={() => go("sessions")} href="#/sessions">FRC Sessions</a>
          <a onClick={() => go("masterclass")} href="#/masterclass">Masterclass</a>
          <a onClick={() => go("about")} href="#/about">About</a>
          <a onClick={() => go("contact")} href="#/contact" style={{ color: "var(--accent)" }}>Enquire →</a>
        </div>
        <div style={{ marginTop: "auto", color: "var(--fg-muted)", fontSize: 14 }}>
          St' Giles Hall, Pound Hill,<br/>Cambridge CB3 0AE<br/>
          <a href="mailto:bomostretch@gmail.com" style={{ color: "var(--fg-muted)" }}>bomostretch@gmail.com</a>
        </div>
      </div>
    </>
  );
}

// ─── Footer (colophon style) ──────────────────────────────────
function Footer({ navigate }) {
  return (
    <footer className="bm-footer-v15">
      <h2 className="bm-footer-mark">
        BoMovement<span className="accent">.</span>
      </h2>
      <div className="bm-footer-cols">
        <div className="bm-footer-col">
          <h4>Studio</h4>
          <p>St' Giles Hall</p>
          <p>Pound Hill</p>
          <p>Cambridge CB3 0AE</p>
          <p style={{ marginTop: 8 }}>Mon–Sat · 8am–3pm</p>
        </div>
        <div className="bm-footer-col">
          <h4>Site</h4>
          <a onClick={(e) => { e.preventDefault(); navigate("sessions"); }} href="#/sessions">FRC Sessions</a>
          <a onClick={(e) => { e.preventDefault(); navigate("masterclass"); }} href="#/masterclass">Masterclass</a>
          <a onClick={(e) => { e.preventDefault(); navigate("about"); }} href="#/about">About</a>
          <a onClick={(e) => { e.preventDefault(); navigate("contact"); }} href="#/contact">Enquire</a>
          <a onClick={(e) => { e.preventDefault(); navigate("terms"); }} href="#/terms">Terms &amp; policy</a>
        </div>
        <div className="bm-footer-col">
          <h4>Contact</h4>
          <a href="mailto:bomostretch@gmail.com">bomostretch@gmail.com</a>
          <a href="https://instagram.com/bomostretch" target="_blank" rel="noreferrer">@bomostretch</a>
        </div>
        <div className="bm-footer-col">
          <h4>Colophon</h4>
          <p>Set in Fraunces &amp; Inter.</p>
          <p>Photography by Adam Bo.</p>
          <p>Built &amp; refined in Cambridge.</p>
        </div>
      </div>
      <div className="bm-colophon">
        <span>© BoMovement · MMXXVI</span>
        <span>Est. 2020 · Cambridge, UK</span>
        <span>All rights reserved</span>
      </div>
    </footer>
  );
}

// ─── Masthead jump-list ──────────────────────────────────────
function MastheadJump({ items, navigate }) {
  return (
    <nav className="bm-mast-jump" aria-label="Quick links">
      {items.map((it, i) => (
        <a
          key={it.id}
          href={`#/${it.id}`}
          onClick={(e) => { e.preventDefault(); navigate(it.id); }}
        >
          <span className="num">N° {String(i + 1).padStart(2, "0")}</span>
          <span>{it.label}</span>
          <em>{it.sub}</em>
          <span className="arrow">↗</span>
        </a>
      ))}
    </nav>
  );
}

// ─── FRC Process scroller (circular B&W steps) ──────────────
function FrcProcess({ steps }) {
  const trackRef = React.useRef(null);
  const wrapRef = React.useRef(null);
  const [progress, setProgress] = React.useState(0);
  const [canPrev, setCanPrev] = React.useState(false);
  const [canNext, setCanNext] = React.useState(true);
  const [hintVisible, setHintVisible] = React.useState(true);
  const [activeIx, setActiveIx] = React.useState(0);

  const update = React.useCallback(() => {
    const el = trackRef.current;
    const wrap = wrapRef.current;
    if (!el) return;
    const max = el.scrollWidth - el.clientWidth;
    const p = max > 0 ? el.scrollLeft / max : 0;
    setProgress(p);
    setCanPrev(el.scrollLeft > 4);
    setCanNext(el.scrollLeft < max - 4);
    if (wrap) {
      wrap.dataset.atStart = el.scrollLeft <= 4;
      wrap.dataset.atEnd = el.scrollLeft >= max - 4;
    }
    // Determine which step is centered
    const center = el.scrollLeft + el.clientWidth / 2;
    let bestIx = 0; let bestDist = Infinity;
    [...el.children].forEach((child, i) => {
      const c = child.offsetLeft + child.offsetWidth / 2;
      const d = Math.abs(c - center);
      if (d < bestDist) { bestDist = d; bestIx = i; }
    });
    setActiveIx(bestIx);
  }, []);

  React.useEffect(() => {
    const el = trackRef.current;
    if (!el) return;
    el.addEventListener("scroll", update, { passive: true });
    update();
    const ro = new ResizeObserver(update);
    ro.observe(el);
    return () => { el.removeEventListener("scroll", update); ro.disconnect(); };
  }, [update]);

  // Hide hint after first user scroll
  React.useEffect(() => {
    if (progress > 0.04 && hintVisible) setHintVisible(false);
  }, [progress, hintVisible]);

  // Drag-to-scroll on desktop
  React.useEffect(() => {
    const el = trackRef.current;
    if (!el) return;
    let down = false, startX = 0, startLeft = 0, moved = false;
    const onDown = (e) => {
      if (e.button !== undefined && e.button !== 0) return;
      down = true; moved = false;
      startX = (e.touches ? e.touches[0].pageX : e.pageX) - el.offsetLeft;
      startLeft = el.scrollLeft;
      el.classList.add("is-dragging");
    };
    const onMove = (e) => {
      if (!down) return;
      const x = (e.touches ? e.touches[0].pageX : e.pageX) - el.offsetLeft;
      const dx = x - startX;
      if (Math.abs(dx) > 4) moved = true;
      el.scrollLeft = startLeft - dx;
    };
    const onUp = () => {
      down = false;
      el.classList.remove("is-dragging");
      // re-snap
      el.style.scrollSnapType = "";
    };
    const onClick = (e) => { if (moved) { e.preventDefault(); e.stopPropagation(); } };
    el.addEventListener("mousedown", onDown);
    el.addEventListener("click", onClick, true);
    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseup", onUp);
    return () => {
      el.removeEventListener("mousedown", onDown);
      el.removeEventListener("click", onClick, true);
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseup", onUp);
    };
  }, []);

  const scrollToIx = (i) => {
    const el = trackRef.current;
    if (!el) return;
    const child = el.children[i];
    if (!child) return;
    el.scrollTo({ left: child.offsetLeft - 64, behavior: "smooth" });
  };

  const scroll = (dir) => {
    const el = trackRef.current;
    if (!el) return;
    // Compute step width from actual DOM (handles mobile vs desktop: 360+40 vs 280+24)
    const first = el.children[0];
    const second = el.children[1];
    const step = (second && first)
      ? second.offsetLeft - first.offsetLeft
      : 400;
    el.scrollBy({ left: dir * step, behavior: "smooth" });
  };

  return (
    <section className="bm-frc" data-screen-label="Sessions · FRC process">
      <div className="bm-frc-head">
        <div className="bm-frc-title">
          <span className="bm-eyebrow">The process · FRC</span>
          <h2>How a session <em>actually</em> unfolds.</h2>
        </div>
        <div className="bm-frc-controls">
          <button className="bm-frc-btn" onClick={() => scroll(-1)} disabled={!canPrev} aria-label="Previous">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <path d="M19 12H5M12 19l-7-7 7-7"/>
            </svg>
          </button>
          <button className="bm-frc-btn" onClick={() => scroll(1)} disabled={!canNext} aria-label="Next">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <path d="M5 12h14M12 5l7 7-7 7"/>
            </svg>
          </button>
        </div>
      </div>
      <div className="bm-frc-track-wrap" ref={wrapRef} data-at-start="true" data-at-end="false">
        <div className={`bm-frc-hint ${hintVisible ? "" : "is-hidden"}`}>
          Drag&nbsp;
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" style={{ verticalAlign: "middle" }}>
            <path d="M5 12h14M12 5l7 7-7 7"/>
          </svg>
        </div>
        <div className="bm-frc-track" ref={trackRef}>
          {steps.map((s, i) => (
            <article className="bm-frc-step" key={s.title}>
              <div className="bm-frc-circle">
                <span className="bm-frc-num">N° {String(i + 1).padStart(2, "0")}</span>
                <span className="bm-frc-meta">
                  <span>{s.tag}</span>
                  <span>{s.duration}</span>
                </span>
              </div>
              <div className="bm-frc-body">
                <h3>
                  <span className="ix">Step {String(i + 1).padStart(2, "0")} / {String(steps.length).padStart(2, "0")}</span>
                  {s.title}
                </h3>
                <p>{s.body}</p>
              </div>
            </article>
          ))}
        </div>
      </div>
      <div className="bm-frc-progress">
        <span>{String(activeIx + 1).padStart(2, "0")}</span>
        <div className="bm-frc-bar">
          <div className="bm-frc-bar-fill" style={{ width: `${Math.max(8, progress * 100)}%` }} />
        </div>
        <div className="bm-frc-dots" role="tablist" aria-label="Step">
          {steps.map((_, i) => (
            <button
              key={i}
              className={`bm-frc-dot ${activeIx === i ? "is-active" : ""}`}
              onClick={() => scrollToIx(i)}
              aria-label={`Go to step ${i + 1}`}
              aria-current={activeIx === i ? "true" : undefined}
            />
          ))}
        </div>
        <span>{String(steps.length).padStart(2, "0")}</span>
      </div>
    </section>
  );
}

// ─── SwapFriendlyMedia ────────────────────────────────────────
function SwapFriendlyMedia({
  videoSrc,
  audioSrc = "/assets/placeholder_audio.mp3",
  imageLabel = "Hands-on stretching · body in motion",
  caption = "Founder placeholder. Adam's full video replaces this when he records."
}) {
  if (videoSrc) {
    return (
      <div className="bm-vsl">
        <video
          src={videoSrc}
          controls
          style={{ width: "100%", aspectRatio: "16 / 9", display: "block", background: "#000" }}
        />
      </div>
    );
  }
  return (
    <div className="bm-vsl">
      <PhotoSlot label={imageLabel} className="bm-vsl-image" style={{ borderRadius: 0 }} />
      <div className="bm-vsl-audio">
        <audio controls preload="metadata">
          <source src={audioSrc} type="audio/mpeg" />
          Your browser does not support audio.
        </audio>
        <div className="bm-caption">{caption}</div>
      </div>
    </div>
  );
}

// ─── Pull-quote ──────────────────────────────────────────────
function PullQuote({ children }) {
  return (
    <div className="bm-pullquote-block">
      <p className="bm-pullquote" style={{ margin: 0 }}>{children}</p>
    </div>
  );
}

// ─── Form bits ────────────────────────────────────────────────
function Field({ label, error, children }) {
  return (
    <div className="bm-field">
      <label className="bm-label">{label}</label>
      {children}
      {error && <div className="bm-field-error">{error}</div>}
    </div>
  );
}

function RadioGroup({ name, value, options, onChange }) {
  return (
    <div className="bm-radio-group" role="radiogroup">
      {options.map(o => (
        <label key={o} className={`bm-radio ${value === o ? "is-checked" : ""}`}>
          <input
            type="radio"
            name={name}
            value={o}
            checked={value === o}
            onChange={() => onChange(o)}
          />
          {o}
        </label>
      ))}
    </div>
  );
}

function useFormState(initial) {
  const [values, setValues] = React.useState(initial);
  const [errors, setErrors] = React.useState({});
  const [submitted, setSubmitted] = React.useState(false);
  const [submitting, setSubmitting] = React.useState(false);
  const [submitError, setSubmitError] = React.useState(null);
  const set = (k, v) => {
    setValues(prev => ({ ...prev, [k]: v }));
    if (errors[k]) setErrors(prev => ({ ...prev, [k]: undefined }));
  };
  return { values, set, errors, setErrors, submitted, setSubmitted, submitting, setSubmitting, submitError, setSubmitError };
}

// Posts the form values to the n8n webhook (test-mode → routes to duy@sharpr.cloud).
// Form type drives the email template + subject line. Honeypot field name is "website".
async function postEnquiry(formType, values) {
  const res = await fetch("https://n8n.sharpr.cloud/webhook/bomo-enquiry", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ form_type: formType, website: "", ...values })
  });
  if (!res.ok) {
    const text = await res.text().catch(() => "");
    throw new Error(text ? `Server error: ${text.slice(0, 120)}` : `HTTP ${res.status}`);
  }
  return res.json().catch(() => ({ ok: true }));
}

function validate(values, required) {
  const errs = {};
  required.forEach(k => {
    const v = values[k];
    if (v == null || (typeof v === "string" && v.trim() === "")) {
      errs[k] = "This field is required.";
    }
  });
  if (values.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(values.email)) {
    errs.email = "Please enter a valid email.";
  }
  return errs;
}

// ─── Audience icon set (line art) ─────────────────────────────
const AudienceIcon = ({ kind }) => {
  const common = { width: 36, height: 36, viewBox: "0 0 36 36", fill: "none", stroke: "currentColor", strokeWidth: 1.4, strokeLinecap: "round", strokeLinejoin: "round" };
  if (kind === "desk") return (
    <svg {...common}><path d="M6 24h24M8 24V14h20v10M12 18h16M14 28v2M22 28v2"/></svg>
  );
  if (kind === "athlete") return (
    <svg {...common}><circle cx="20" cy="9" r="2.5"/><path d="M14 18l4-2 3 5 4-1M11 28l5-6 5-1 3 5"/></svg>
  );
  if (kind === "rehab") return (
    <svg {...common}><path d="M4 22h6l3-7 4 11 4-13 3 9h8"/></svg>
  );
  if (kind === "1to1")  return (
    <svg {...common}><circle cx="13" cy="13" r="3.5"/><circle cx="23" cy="13" r="3.5"/><path d="M7 27c0-3.5 2.5-6 6-6s6 2.5 6 6M17 27c0-3.5 2.5-6 6-6s6 2.5 6 6"/></svg>
  );
  if (kind === "loc")   return (
    <svg {...common}><path d="M18 31s9-9 9-16a9 9 0 0 0-18 0c0 7 9 16 9 16Z"/><circle cx="18" cy="15" r="3"/></svg>
  );
  if (kind === "vid")   return (
    <svg {...common}><rect x="6" y="10" width="18" height="16" rx="2"/><path d="m24 16 6-3v10l-6-3z"/></svg>
  );
  return null;
};

// Export to window
Object.assign(window, {
  PhotoSlot, EditorialPhoto, Marquee, FadeIn, Logo, Nav, Footer, SwapFriendlyMedia,
  PullQuote, Field, RadioGroup, useFormState, validate, postEnquiry, AudienceIcon,
  MastheadJump, FrcProcess
});
