// Shared primitives & small helpers
const { useState, useEffect, useRef, useMemo, useCallback } = React;

// Render a title array: strings + { em: "..." } chips
function RichTitle({ parts, className = "", reveal = false }) {
  if (reveal) {
    return (
      <span className={className}>
        {parts.map((p, i) => {
          const text = typeof p === "string" ? p : p.em;
          const em = typeof p !== "string";
          return (
            <span key={i} style={{ whiteSpace: "pre-wrap" }}>
              {text.split(" ").map((w, j) => (
                <span key={j} style={{ display: "inline-block", whiteSpace: "pre" }}>
                  {em ? <em>{w} </em> : <>{w} </>}
                </span>
              ))}
            </span>
          );
        })}
      </span>
    );
  }
  return (
    <span className={className}>
      {parts.map((p, i) =>
        typeof p === "string" ? <span key={i}>{p}</span> : <em key={i}>{p.em}</em>
      )}
    </span>
  );
}

// Bold markdown (** **) within a paragraph
function Rich({ children }) {
  if (typeof children !== "string") return children;
  const parts = children.split(/(\*\*[^*]+\*\*)/g);
  return parts.map((p, i) =>
    p.startsWith("**") ? <b key={i}>{p.slice(2, -2)}</b> : <span key={i}>{p}</span>
  );
}

// IntersectionObserver-based reveal
function useReveal(opts = {}) {
  const ref = useRef(null);
  const [shown, setShown] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (shown) return;
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            setShown(true);
            io.disconnect();
          }
        });
      },
      { threshold: opts.threshold ?? 0.15, rootMargin: opts.rootMargin ?? "0px 0px -60px 0px" }
    );
    io.observe(el);
    return () => io.disconnect();
  }, [shown]);
  return [ref, shown];
}

function Reveal({ children, className = "", as = "div", style }) {
  const [ref, shown] = useReveal();
  const Tag = as;
  return (
    <Tag ref={ref} className={`reveal ${shown ? "in" : ""} ${className}`} style={style}>
      {children}
    </Tag>
  );
}

// Reveal headline that animates word-by-word
function RevealWords({ parts, as = "h2", className = "" }) {
  const [ref, shown] = useReveal();
  const Tag = as;
  const words = [];
  parts.forEach((p, i) => {
    const text = typeof p === "string" ? p : p.em;
    const em = typeof p !== "string";
    text.split(/(\s+)/).forEach((w, j) => {
      if (/^\s+$/.test(w)) {
        words.push(<span key={`${i}-${j}-sp`} style={{ whiteSpace: "pre" }}>{w}</span>);
      } else if (w.length) {
        words.push(
          em
            ? <em key={`${i}-${j}`}><span>{w}</span></em>
            : <span key={`${i}-${j}`}>{w}</span>
        );
      }
    });
  });
  // Wrap so nested <em><span> still animates properly by adding stagger via delay
  return (
    <Tag ref={ref} className={`reveal-words ${shown ? "in" : ""} ${className}`}>
      {words.map((w, i) => {
        const delay = `${i * 35}ms`;
        if (w.type === "em") {
          return React.cloneElement(w, { key: i }, React.cloneElement(w.props.children, { style: { transitionDelay: delay } }));
        }
        return React.cloneElement(w, { key: i, style: { transitionDelay: delay, whiteSpace: w.props.style?.whiteSpace || undefined } });
      })}
    </Tag>
  );
}

// Static waveform canvas (drawn once, seeded)
function StaticWave({ seed = 1, color = "var(--ink)", accent = "var(--accent)", bars = 120, height = 40, active = 0 }) {
  const ref = useRef(null);
  useEffect(() => {
    const c = ref.current;
    if (!c) return;
    const ctx = c.getContext("2d");
    const dpr = window.devicePixelRatio || 1;
    const w = c.offsetWidth;
    c.width = w * dpr;
    c.height = height * dpr;
    ctx.scale(dpr, dpr);
    ctx.clearRect(0, 0, w, height);

    const styles = getComputedStyle(document.documentElement);
    const inkColor = color.startsWith("var") ? styles.getPropertyValue("--ink").trim() : color;
    const accentColor = accent.startsWith("var") ? styles.getPropertyValue("--accent").trim() : accent;

    const barW = (w / bars) * 0.55;
    const gap = (w / bars) * 0.45;
    let x = 0;
    let s = seed;
    for (let i = 0; i < bars; i++) {
      s = (s * 9301 + 49297) % 233280;
      const n = s / 233280;
      // shape: central emphasis
      const bell = Math.sin((i / bars) * Math.PI);
      const h = Math.max(2, (n * 0.6 + bell * 0.6) * height * 0.9);
      const y = (height - h) / 2;
      ctx.fillStyle = i / bars < active ? accentColor : inkColor;
      ctx.globalAlpha = i / bars < active ? 1 : 0.35;
      ctx.fillRect(x, y, barW, h);
      x += barW + gap;
    }
  }, [seed, bars, height, active]);
  return <canvas ref={ref} className="wave-canvas" style={{ height }} />;
}

// Animated, pulse-based waveform (no audio; smooth ambient)
function AnimatedWave({ color = "var(--ink)", accent = "var(--accent)", bars = 180, height = 48, running = true }) {
  const ref = useRef(null);
  const rafRef = useRef();
  useEffect(() => {
    const c = ref.current;
    if (!c) return;
    const ctx = c.getContext("2d");
    const dpr = window.devicePixelRatio || 1;
    function resize() {
      const w = c.offsetWidth;
      c.width = w * dpr;
      c.height = height * dpr;
      ctx.setTransform(1, 0, 0, 1, 0, 0);
      ctx.scale(dpr, dpr);
    }
    resize();
    window.addEventListener("resize", resize);
    const styles = getComputedStyle(document.documentElement);

    let t0 = performance.now();
    function frame(now) {
      const w = c.offsetWidth;
      ctx.clearRect(0, 0, w, height);
      const inkColor = styles.getPropertyValue("--ink").trim() || "#111";
      const accentColor = styles.getPropertyValue("--accent").trim() || "#C8102E";
      const t = (now - t0) / 1000;
      const barW = (w / bars) * 0.45;
      const gap = (w / bars) * 0.55;
      let x = 0;
      for (let i = 0; i < bars; i++) {
        const p = i / bars;
        const bell = Math.pow(Math.sin(p * Math.PI), 1.5);
        const wave =
          Math.sin(p * 24 + t * 1.4) * 0.45 +
          Math.sin(p * 9 + t * 2.1) * 0.35 +
          Math.sin(p * 3 - t * 0.8) * 0.25;
        const h = Math.max(2, (bell * 0.9 + Math.abs(wave) * 0.6) * height * 0.85);
        const y = (height - h) / 2;
        const useAccent = i % 11 === 3;
        ctx.fillStyle = useAccent ? accentColor : inkColor;
        ctx.globalAlpha = useAccent ? 0.85 : 0.55;
        ctx.fillRect(x, y, barW, h);
        x += barW + gap;
      }
      if (running) rafRef.current = requestAnimationFrame(frame);
    }
    rafRef.current = requestAnimationFrame(frame);
    return () => {
      window.removeEventListener("resize", resize);
      cancelAnimationFrame(rafRef.current);
    };
  }, [bars, height, running]);
  return <canvas ref={ref} className="wave-canvas" style={{ height }} />;
}

// Icons
const Icon = {
  Arrow: (p) => (
    <svg width="14" height="14" viewBox="0 0 14 14" fill="none" {...p}>
      <path d="M3 11L11 3M11 3H5M11 3V9" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  ),
  Play: (p) => (
    <svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor" {...p}>
      <path d="M3.5 2L11.5 7L3.5 12V2Z" />
    </svg>
  ),
  Pause: (p) => (
    <svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor" {...p}>
      <rect x="3" y="2" width="3" height="10" rx="0.5" />
      <rect x="8" y="2" width="3" height="10" rx="0.5" />
    </svg>
  ),
  Sun: (p) => (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}>
      <circle cx="8" cy="8" r="3.2" />
      <path d="M8 1v1.5M8 13.5V15M1 8h1.5M13.5 8H15M3 3l1.1 1.1M11.9 11.9 13 13M3 13l1.1-1.1M11.9 4.1 13 3" strokeLinecap="round" />
    </svg>
  ),
  Moon: (p) => (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" {...p}>
      <path d="M13.5 9.5A5.5 5.5 0 1 1 6.5 2.5a4.5 4.5 0 0 0 7 7Z" strokeLinejoin="round" />
    </svg>
  ),
  Phone: (p) => (
    <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}>
      <path d="M2 3.5C2 2.67 2.67 2 3.5 2h1a1 1 0 0 1 1 .8l.4 2a1 1 0 0 1-.3 1L4.5 7a8 8 0 0 0 2.5 2.5l1.2-1.2a1 1 0 0 1 1-.3l2 .4a1 1 0 0 1 .8 1V11a1.5 1.5 0 0 1-1.5 1.5A9.5 9.5 0 0 1 2 3.5Z" strokeLinejoin="round" />
    </svg>
  ),
  Mail: (p) => (
    <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}>
      <rect x="1.75" y="3" width="10.5" height="8" rx="1" />
      <path d="m2 4 5 4 5-4" />
    </svg>
  ),
  Plus: (p) => (
    <svg width="12" height="12" viewBox="0 0 12 12" {...p}>
      <path d="M6 1v10M1 6h10" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
    </svg>
  ),
};

Object.assign(window, { RichTitle, Rich, useReveal, Reveal, RevealWords, StaticWave, AnimatedWave, Icon, Wordmark, BrandLockup, LogoMark });

// ---- Brand ----
function LogoMark({ size = 40 }) {
  return (
    <img
      src="/assets/images/eric-logo-mark.png"
      alt="Eric spricht."
      width={size}
      height={size}
      style={{ flexShrink: 0, display: "block", borderRadius: "22%" }}
    />
  );
}

function Wordmark({ size = "sm" }) {
  const fs = { sm: 20, md: 28, lg: 44, xl: 96 }[size] || 20;
  return (
    <span
      className="wordmark-svg"
      aria-label="Eric spricht."
      style={{
        display: "inline-flex",
        alignItems: "baseline",
        lineHeight: 1,
        whiteSpace: "nowrap",
        fontSize: fs,
      }}
    >
      <span style={{
        fontFamily: "var(--font-sans)",
        fontWeight: 600,
        letterSpacing: "-0.03em",
        color: "var(--ink)",
      }}>Eric</span>
      <span style={{ width: "0.22em" }} />
      <span style={{
        fontFamily: "var(--font-serif)",
        fontStyle: "italic",
        fontWeight: 400,
        letterSpacing: "-0.01em",
        color: "var(--accent)",
      }}>spricht</span>
      <span style={{
        display: "inline-block",
        width: "0.2em",
        height: "0.2em",
        minWidth: 5, minHeight: 5,
        borderRadius: "50%",
        background: "var(--accent)",
        marginLeft: "0.08em",
        alignSelf: "flex-end",
        transform: "translateY(-0.08em)",
        flexShrink: 0,
      }} />
    </span>
  );
}

function BrandLockup() {
  return (
    <>
      <LogoMark size={42} />
      <span className="brand-lockup">
        <Wordmark size="sm" />
        <span className="subline">Werbe- &amp; Synchronsprecher</span>
      </span>
    </>
  );
}
