/* global React */
const { useState, useEffect, useRef, useCallback, useMemo } = React;

/* ========================================================================
   Custom cursor — soft cream dot + ring, reactive to hover state
   ======================================================================== */
function CustomCursor() {
  const dotRef = useRef(null);
  const ringRef = useRef(null);
  const labelRef = useRef(null);
  const target = useRef({ x: 0, y: 0 });
  const pos = useRef({ x: 0, y: 0 });
  const [label, setLabel] = useState("");
  const [variant, setVariant] = useState("default"); // default | hover | view | drag

  useEffect(() => {
    const onMove = (e) => {
      target.current.x = e.clientX;
      target.current.y = e.clientY;
      if (dotRef.current) {
        dotRef.current.style.transform = `translate(${e.clientX}px, ${e.clientY}px) translate(-50%, -50%)`;
      }
    };
    const onOver = (e) => {
      const el = e.target.closest("[data-cursor]");
      if (el) {
        setVariant(el.dataset.cursor || "hover");
        setLabel(el.dataset.cursorLabel || "");
      } else {
        setVariant("default");
        setLabel("");
      }
    };
    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseover", onOver);

    let raf;
    const tick = () => {
      pos.current.x += (target.current.x - pos.current.x) * 0.18;
      pos.current.y += (target.current.y - pos.current.y) * 0.18;
      if (ringRef.current) {
        ringRef.current.style.transform = `translate(${pos.current.x}px, ${pos.current.y}px) translate(-50%, -50%)`;
      }
      raf = requestAnimationFrame(tick);
    };
    tick();

    return () => {
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseover", onOver);
      cancelAnimationFrame(raf);
    };
  }, []);

  const ringStyle = {
    position: "fixed",
    top: 0, left: 0,
    width: variant === "view" ? 96 : variant === "hover" ? 56 : 28,
    height: variant === "view" ? 96 : variant === "hover" ? 56 : 28,
    borderRadius: "50%",
    border: variant === "view" ? "1px solid rgba(200,168,122,0.7)" : "1px solid rgba(245,241,234,0.35)",
    background: variant === "view" ? "rgba(200,168,122,0.08)" : "transparent",
    pointerEvents: "none",
    zIndex: 10001,
    transition: "width .35s cubic-bezier(.4,1,.2,1), height .35s cubic-bezier(.4,1,.2,1), background .3s, border-color .3s",
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    color: "var(--accent)",
    fontFamily: "var(--font-mono)",
    fontSize: 10,
    letterSpacing: "0.1em",
    textTransform: "uppercase",
    mixBlendMode: "difference",
  };

  const dotStyle = {
    position: "fixed",
    top: 0, left: 0,
    width: 4, height: 4,
    borderRadius: "50%",
    background: "var(--fg)",
    pointerEvents: "none",
    zIndex: 10002,
    mixBlendMode: "difference",
  };

  return (
    <>
      <div ref={ringRef} style={ringStyle}>{label}</div>
      <div ref={dotRef} style={dotStyle} />
    </>
  );
}

/* ========================================================================
   Reveal — IntersectionObserver-driven, respects motion intensity
   ======================================================================== */
function Reveal({ children, delay = 0, y = 40, motion = 1, as = "div", ...rest }) {
  const ref = useRef(null);
  const [visible, setVisible] = useState(false);

  useEffect(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            setVisible(true);
            io.unobserve(e.target);
          }
        });
      },
      { threshold: 0.12, rootMargin: "0px 0px -10% 0px" }
    );
    io.observe(ref.current);
    return () => io.disconnect();
  }, []);

  const dur = 1.1 * motion + 0.2;
  const Tag = as;
  return (
    <Tag
      ref={ref}
      style={{
        opacity: visible ? 1 : 0,
        transform: visible ? "translateY(0)" : `translateY(${y * motion}px)`,
        transition: `opacity ${dur}s cubic-bezier(.2,.7,.2,1) ${delay}s, transform ${dur}s cubic-bezier(.2,.7,.2,1) ${delay}s`,
        willChange: "opacity, transform",
        ...(rest.style || {}),
      }}
      {...rest}
    >
      {children}
    </Tag>
  );
}

/* ========================================================================
   Marquee — scroll-velocity driven. Scrolling down pushes the ticker left,
   scrolling up pushes it right, no scroll = no movement. Three sequence
   copies keep the wrap seamless in both directions.
   ======================================================================== */
function Marquee({ items, speed = 60, separator = "✦", motion = 1 }) {
  const trackRef = useRef(null);
  const posRef = useRef(0);
  const lastY = useRef(typeof window !== "undefined" ? window.scrollY : 0);
  const widthRef = useRef(0);

  useEffect(() => {
    const el = trackRef.current;
    if (!el) return;
    lastY.current = window.scrollY || 0;
    widthRef.current = el.scrollWidth / 3;

    const measure = () => {
      if (trackRef.current) widthRef.current = trackRef.current.scrollWidth / 3;
    };
    window.addEventListener("resize", measure);

    let raf = 0;
    let cancelled = false;
    const factor = (Number(speed) / 60) * (Number(motion) || 1);

    const loop = () => {
      if (cancelled) return;
      const target = trackRef.current;
      if (!target) {
        raf = requestAnimationFrame(loop);
        return;
      }
      const y = window.scrollY || 0;
      const dy = y - lastY.current;
      lastY.current = y;
      let next = posRef.current - dy * factor;
      const w = widthRef.current || 1;
      while (next <= -w) next += w;
      while (next > 0) next -= w;
      if (!Number.isFinite(next)) next = 0;
      posRef.current = next;
      target.style.transform = `translate3d(${next}px, 0, 0)`;
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);

    return () => {
      cancelled = true;
      cancelAnimationFrame(raf);
      window.removeEventListener("resize", measure);
    };
  }, [speed, motion]);

  const seq = (
    <span style={{ display: "inline-flex", alignItems: "center", gap: 48, paddingRight: 48 }}>
      {items.map((it, i) => (
        <React.Fragment key={i}>
          <span>{it}</span>
          <span style={{ color: "var(--accent)", fontSize: "0.7em" }}>{separator}</span>
        </React.Fragment>
      ))}
    </span>
  );
  return (
    <div style={{
      overflow: "hidden",
      whiteSpace: "nowrap",
      width: "100%",
      borderTop: "1px solid var(--line)",
      borderBottom: "1px solid var(--line)",
      padding: "22px 0",
      fontFamily: "var(--font-mono)",
      fontSize: 14,
      letterSpacing: "0.18em",
      textTransform: "uppercase",
      color: "var(--fg-dim)",
    }}>
      <div ref={trackRef} style={{ display: "inline-flex", willChange: "transform" }}>
        {seq}{seq}{seq}
      </div>
    </div>
  );
}

/* ========================================================================
   Textured placeholder — SVG-based, varies by seed
   ======================================================================== */
function Placeholder({ seed = 0, label = "", aspect = "4 / 5", tone = "warm" }) {
  // Pick subtle gradient + pattern variant based on seed
  const variants = [
    { from: "#1a1612", to: "#0a0a0a", grain: "#c8a87a" },
    { from: "#141414", to: "#080808", grain: "#f5f1ea" },
    { from: "#1c1814", to: "#0c0a08", grain: "#c8a87a" },
    { from: "#0e0e10", to: "#050507", grain: "#9a948a" },
    { from: "#181410", to: "#0a0805", grain: "#c8a87a" },
    { from: "#161616", to: "#0a0a0a", grain: "#f5f1ea" },
  ];
  const v = variants[seed % variants.length];

  // Pattern variant
  const pattern = seed % 4;

  return (
    <div
      data-cursor="view"
      data-cursor-label="View"
      style={{
        position: "relative",
        width: "100%",
        aspectRatio: aspect,
        background: `linear-gradient(160deg, ${v.from} 0%, ${v.to} 100%)`,
        overflow: "hidden",
        borderRadius: 2,
      }}
    >
      <svg width="100%" height="100%" style={{ position: "absolute", inset: 0, opacity: 0.5 }} preserveAspectRatio="none">
        <defs>
          <pattern id={`p${seed}-stripes`} width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
            <line x1="0" y1="0" x2="0" y2="6" stroke={v.grain} strokeWidth="0.4" opacity="0.18" />
          </pattern>
          <pattern id={`p${seed}-dots`} width="14" height="14" patternUnits="userSpaceOnUse">
            <circle cx="2" cy="2" r="0.8" fill={v.grain} opacity="0.2" />
          </pattern>
          <pattern id={`p${seed}-grid`} width="40" height="40" patternUnits="userSpaceOnUse">
            <path d="M 40 0 L 0 0 0 40" fill="none" stroke={v.grain} strokeWidth="0.3" opacity="0.15" />
          </pattern>
          <radialGradient id={`p${seed}-glow`} cx="50%" cy="50%" r="60%">
            <stop offset="0%" stopColor={v.grain} stopOpacity="0.18" />
            <stop offset="100%" stopColor={v.grain} stopOpacity="0" />
          </radialGradient>
        </defs>
        <rect width="100%" height="100%" fill={`url(#p${seed}-${["stripes","dots","grid","stripes"][pattern]})`} />
        <rect width="100%" height="100%" fill={`url(#p${seed}-glow)`} />
      </svg>

      {/* Center mark */}
      <div style={{
        position: "absolute",
        inset: 0,
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        flexDirection: "column",
        gap: 8,
      }}>
        <div style={{
          width: 28, height: 28,
          border: `1px solid ${v.grain}`,
          opacity: 0.35,
          transform: "rotate(45deg)",
        }} />
        <div style={{
          fontFamily: "var(--font-mono)",
          fontSize: 10,
          letterSpacing: "0.2em",
          textTransform: "uppercase",
          color: v.grain,
          opacity: 0.5,
          marginTop: 8,
        }}>
          {label}
        </div>
      </div>

      {/* Corner ticks */}
      <CornerTicks color={v.grain} />
    </div>
  );
}

function CornerTicks({ color = "#f5f1ea" }) {
  const tick = (extra) => ({
    position: "absolute",
    width: 10, height: 10,
    border: "1px solid",
    borderColor: color,
    opacity: 0.3,
    ...extra,
  });
  return (
    <>
      <div style={tick({ top: 10, left: 10, borderRight: "none", borderBottom: "none" })} />
      <div style={tick({ top: 10, right: 10, borderLeft: "none", borderBottom: "none" })} />
      <div style={tick({ bottom: 10, left: 10, borderRight: "none", borderTop: "none" })} />
      <div style={tick({ bottom: 10, right: 10, borderLeft: "none", borderTop: "none" })} />
    </>
  );
}

/* ========================================================================
   Big editorial type that splits into lines for stagger reveal
   ======================================================================== */
function SplitLines({ lines, motion = 1, baseDelay = 0, className = "", style = {} }) {
  return (
    <span className={className} style={style}>
      {lines.map((line, i) => (
        <span key={i} style={{ display: "block", overflow: "hidden" }}>
          <Reveal
            as="span"
            motion={motion}
            y={70}
            delay={baseDelay + i * 0.08}
            style={{ display: "inline-block" }}
          >
            {line}
          </Reveal>
        </span>
      ))}
    </span>
  );
}

/* ========================================================================
   Live clock — IST
   ======================================================================== */
function LiveClock() {
  const [now, setNow] = useState(() => new Date());
  useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  const time = now.toLocaleTimeString("en-IN", {
    hour: "2-digit", minute: "2-digit", second: "2-digit",
    hour12: false, timeZone: "Asia/Kolkata",
  });
  return <span>IST · {time}</span>;
}

Object.assign(window, {
  CustomCursor, Reveal, Marquee, Placeholder, SplitLines, LiveClock, CornerTicks,
});
