/* RADAR1 — shared primitives: clock hooks, framed panels, wipes */
const { useState, useEffect, useMemo, useRef, useCallback } = React;

/* Ticking clock */
function useNow(interval = 1000) {
  const [now, setNow] = useState(() => new Date());
  useEffect(() => {
    const id = setInterval(() => setNow(new Date()), interval);
    return () => clearInterval(id);
  }, [interval]);
  return now;
}

function fmtTime(date, tz, h24, seconds = false) {
  try {
    return new Intl.DateTimeFormat("en-GB", {
      hour: "2-digit", minute: "2-digit", ...(seconds ? { second: "2-digit" } : {}),
      hour12: !h24, timeZone: tz,
    }).format(date).toUpperCase();
  } catch (e) { return "--:--"; }
}

function fmtDate(date, tz) {
  try {
    return new Intl.DateTimeFormat("en-US", {
      weekday: "short", month: "short", day: "numeric", timeZone: tz,
    }).format(date).toUpperCase();
  } catch (e) { return ""; }
}

/* Interval-driven index cycling with fade key */
function useCycle(length, ms, paused = false) {
  const [i, setI] = useState(0);
  useEffect(() => {
    if (paused || length < 2) return;
    const id = setInterval(() => setI((v) => (v + 1) % length), ms);
    return () => clearInterval(id);
  }, [length, ms, paused]);
  return i;
}

/* Caps eyebrow label */
function Eyebrow({ children, color = "var(--text-muted)", size = "var(--fs-caption)", style }) {
  return (
    <div style={{
      fontWeight: "var(--fw-bold)", fontSize: size, letterSpacing: "var(--ls-widest)",
      textTransform: "uppercase", color, fontFamily: "var(--font-ui)", ...style,
    }}>{children}</div>
  );
}

/* Framed rail panel — hairline frame, sweep corner, accent spine */
function RailPanel({ accent = "var(--signal-cyan)", title, right = null, children, flush = false, style }) {
  return (
    <div style={{
      display: "flex", flexDirection: "column",
      background: "rgba(15,24,38,0.72)", border: "1px solid var(--border-hairline)",
      borderLeft: `3px solid ${accent}`, borderRadius: "var(--sweep-md)",
      overflow: "hidden", fontFamily: "var(--font-ui)", ...style,
    }}>
      {title && (
        <div style={{
          display: "flex", alignItems: "center", justifyContent: "space-between",
          padding: "12px 16px 11px", borderBottom: "1px solid var(--border-hairline)",
        }}>
          <Eyebrow color="var(--text-body)" size="15px">{title}</Eyebrow>
          {right}
        </div>
      )}
      <div style={{ padding: flush ? 0 : "14px 16px", flex: 1, minHeight: 0 }}>{children}</div>
    </div>
  );
}

/* Pulsing live dot */
function LiveDot({ color = "var(--radar-red)", size = 8 }) {
  return (
    <span style={{
      width: size, height: size, borderRadius: "var(--radius-pill)", background: color,
      display: "inline-block", animation: "r1-pulse 1.4s ease-in-out infinite", flex: "none",
    }} />
  );
}

/* Delta value (markets) */
function Delta({ v }) {
  const up = v >= 0;
  return (
    <span style={{
      color: up ? "var(--status-up)" : "var(--status-down)",
      fontWeight: "var(--fw-bold)", fontVariantNumeric: "tabular-nums",
    }}>
      {up ? "▲" : "▼"} {Math.abs(v).toFixed(2)}%
    </span>
  );
}

/* Full-stage sweep wipe: red diagonal band crosses the frame; fires on `signal` change */
function SweepWipe({ signal }) {
  const [active, setActive] = useState(false);
  const first = useRef(true);
  useEffect(() => {
    if (first.current) { first.current = false; return; }
    setActive(true);
    const id = setTimeout(() => setActive(false), 950);
    return () => clearTimeout(id);
  }, [signal]);
  if (!active) return null;
  return (
    <div style={{ position: "absolute", inset: 0, zIndex: 90, overflow: "hidden", pointerEvents: "none" }}>
      <div style={{
        position: "absolute", top: "-20%", height: "140%", width: "70%", left: 0,
        background: "var(--grad-red)", transform: "skewX(-14deg) translateX(-160%)",
        animation: "r1-wipe 900ms var(--ease-broadcast) both",
        boxShadow: "0 0 80px rgba(228,3,46,0.45)",
      }}>
        <div style={{
          position: "absolute", inset: "0 auto 0 78%", width: "10%",
          background: "rgba(255,255,255,0.9)",
        }} />
        <div style={{
          position: "absolute", top: "50%", left: "30%", transform: "translateY(-50%) skewX(14deg)",
          fontFamily: "var(--font-brand)", fontWeight: 900, fontSize: 120, color: "rgba(255,255,255,0.16)",
          whiteSpace: "nowrap", letterSpacing: "-0.02em", userSelect: "none",
        }}>RADAR1</div>
      </div>
    </div>
  );
}

/* Simple keyed fade/slide-in wrapper (re-mounts on key change upstream) */
function Enter({ dir = "up", dist = 14, dur = 360, delay = 0, children, style }) {
  return (
    <div style={{
      animation: `r1-enter-${dir} ${dur}ms var(--ease-broadcast) ${delay}ms both`, ...style,
    }}>
      {children}
    </div>
  );
}

/* Broadcast armor: a region that crashes renders nothing and quietly retries.
   The stage itself must never unmount — a bad payload costs one region for
   15 seconds, not the broadcast. */
class R1Boundary extends React.Component {
  constructor(props) { super(props); this.state = { broken: false }; this.timer = null; }
  static getDerivedStateFromError() { return { broken: true }; }
  componentDidCatch(err) { try { console.error("R1Boundary[" + (this.props.name || "?") + "]", err); } catch (e) {} }
  componentDidUpdate() {
    if (this.state.broken && !this.timer) {
      this.timer = setTimeout(() => { this.timer = null; this.setState({ broken: false }); }, 15000);
    }
  }
  componentWillUnmount() { if (this.timer) clearTimeout(this.timer); }
  render() { return this.state.broken ? null : this.props.children; }
}

/* Lucide refresh helper — call after any render that adds data-lucide nodes */
function useLucide(deps) {
  useEffect(() => {
    const id = requestAnimationFrame(() => { window.lucide && window.lucide.createIcons(); });
    return () => cancelAnimationFrame(id);
  }, deps);
}

Object.assign(window, {
  useNow, fmtTime, fmtDate, useCycle, Eyebrow, RailPanel, LiveDot, Delta,
  SweepWipe, Enter, useLucide, R1Boundary,
});
