// components.jsx — shared UI primitives for the mobile prototype

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

// ── Logo ────────────────────────────────────────────────────────────────
// The NADI YOGA badge stamp. Image already contains the wordmark, lotus +
// tree mark and heartbeat motif, so we don't add a separate text label by
// default. Pass `withWordmark` to render a side-by-side lockup with a
// larger "nadi" type next to it.
function Logo({ size = 64, withWordmark = false, color = "var(--ink)", style }) {
  const mark = (
    <img src="mobile/logo-nadi.png" alt="Nadi Yoga" style={{
      width: size, height: size, display: "block",
      // Multiply drops the white image background onto whatever surface
      // we're sitting on (cream or ink) while keeping the linework crisp.
      mixBlendMode: "multiply",
      filter: "contrast(1.05)",
      flexShrink: 0,
    }} />
  );
  if (!withWordmark) {
    return <span style={{ display: "inline-flex", color, ...style }}>{mark}</span>;
  }
  return (
    <span style={{ display: "inline-flex", alignItems: "center", gap: 12, color, ...style }}>
      {mark}
      <span style={{
        fontFamily: "Newsreader, serif",
        fontSize: size * 0.42,
        letterSpacing: "0.24em",
        fontWeight: 400,
        textTransform: "uppercase",
      }}>nadi</span>
    </span>
  );
}

// ── Mono micro-label ───────────────────────────────────────────────────
function Mono({ children, color = "var(--ink-soft)", size = 10.5, style }) {
  return <span style={{
    fontFamily: "'JetBrains Mono', ui-monospace, monospace",
    fontSize: size, letterSpacing: "0.04em",
    color, textTransform: "lowercase",
    ...style,
  }}>{children}</span>;
}

// ── Eyebrow ────────────────────────────────────────────────────────────
function Eyebrow({ children, color = "var(--ink-soft)", style }) {
  return <span style={{
    fontFamily: "'JetBrains Mono', ui-monospace, monospace",
    fontSize: 10.5, letterSpacing: "0.18em",
    textTransform: "uppercase", color, ...style,
  }}><span style={{ opacity: 0.5, marginRight: 6 }}>{"\\\\"}</span>{children}</span>;
}

// ── Buttons (mobile-sized; minimum 48px hit target) ────────────────────
function Button({ children, variant = "primary", size = "md", onClick, style, disabled, fullWidth, type }) {
  const sizes = {
    sm: { padding: "10px 14px", fontSize: 12, letterSpacing: "0.06em", minHeight: 36 },
    md: { padding: "14px 22px", fontSize: 12.5, letterSpacing: "0.08em", minHeight: 48 },
    lg: { padding: "18px 28px", fontSize: 13, letterSpacing: "0.1em", minHeight: 56 },
  };
  const variants = {
    primary: { background: "var(--ink)", color: "var(--bg)", border: "1px solid var(--ink)" },
    accent:  { background: "var(--primary)", color: "var(--primary-ink)", border: "1px solid var(--primary)" },
    ghost:   { background: "transparent", color: "var(--ink)", border: "1px solid var(--ink)" },
    soft:    { background: "var(--surface-2)", color: "var(--ink)", border: "1px solid var(--rule)" },
    plain:   { background: "transparent", color: "var(--ink)", border: "1px solid transparent" },
    dangerGhost: { background: "transparent", color: "var(--primary)", border: "1px solid var(--primary)" },
  };
  return (
    <button type={type || "button"} onClick={onClick} disabled={disabled} className="press"
      style={{
        ...sizes[size], ...variants[variant],
        fontFamily: "Geist, ui-sans-serif",
        fontWeight: 500,
        textTransform: "uppercase",
        opacity: disabled ? 0.45 : 1,
        cursor: disabled ? "not-allowed" : "pointer",
        borderRadius: 0,
        width: fullWidth ? "100%" : "auto",
        display: "inline-flex", justifyContent: "center", alignItems: "center", gap: 8,
        ...style,
      }}>{children}</button>
  );
}

// ── Avatar (initials in tinted circle) ─────────────────────────────────
function Avatar({ name, initials, tone, size = 32, border, anon }) {
  if (anon) {
    return (
      <span style={{
        width: size, height: size, borderRadius: "50%",
        background: "var(--surface-2)",
        border: border ? `2px solid ${border}` : "1px dashed var(--rule-strong)",
        boxSizing: "content-box",
        display: "inline-flex", alignItems: "center", justifyContent: "center",
        color: "var(--ink-soft)",
        flexShrink: 0,
      }} title="Anonymous attendee">
        <svg width={size * 0.5} height={size * 0.5} viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
          <circle cx="10" cy="7.5" r="3"/>
          <path d="M3.5 17c1.4-3.2 4-4.5 6.5-4.5s5.1 1.3 6.5 4.5"/>
        </svg>
      </span>
    );
  }
  return (
    <span style={{
      width: size, height: size, borderRadius: "50%",
      background: tone || "var(--accent)", color: "var(--bg)",
      display: "inline-flex", alignItems: "center", justifyContent: "center",
      fontFamily: "Newsreader, serif", fontWeight: 400,
      fontSize: size * 0.42, letterSpacing: "0.02em",
      flexShrink: 0,
      border: border ? `2px solid ${border}` : "none",
      boxSizing: "content-box",
    }} title={name}>{initials || (name || "").slice(0, 2).toUpperCase()}</span>
  );
}

// Stack of friend avatars with overlap. `anonCount` adds a hollow masked
// dot to represent friends attending anonymously, plus "+N" for overflow.
function FriendStack({ friends, size = 24, max = 4, border = "var(--bg)", anonCount = 0 }) {
  const arr = friends.slice(0, max);
  const more = friends.length - arr.length;
  return (
    <span style={{ display: "inline-flex", alignItems: "center" }}>
      {arr.map((f, i) => (
        <span key={f.id} style={{ marginLeft: i === 0 ? 0 : -8, position: "relative", zIndex: max - i }}>
          <Avatar name={f.name} initials={f.initials} tone={f.tone} size={size} border={border} />
        </span>
      ))}
      {anonCount > 0 && (
        <span style={{ marginLeft: arr.length === 0 ? 0 : -8, position: "relative", zIndex: 0 }}>
          <Avatar size={size} anon border={border} />
        </span>
      )}
      {more > 0 && (
        <span style={{
          marginLeft: -8, position: "relative", zIndex: 0,
          width: size, height: size, borderRadius: "50%",
          background: "var(--surface-2)", color: "var(--ink-soft)",
          border: `2px solid ${border}`, boxSizing: "content-box",
          display: "inline-flex", alignItems: "center", justifyContent: "center",
          fontFamily: "'JetBrains Mono', monospace", fontSize: size * 0.34, letterSpacing: "0.04em",
        }}>+{more}</span>
      )}
    </span>
  );
}

// ── Breath dot (the brand signature) ───────────────────────────────────
function BreathDot({ size = 12, color = "var(--primary)", duration = 8 }) {
  return <span className="breath" style={{ width: size, height: size, background: color, animationDuration: duration + "s" }} />;
}

// ── Top bar (per-screen) ───────────────────────────────────────────────
function TopBar({ title, eyebrow, right, onBack, style }) {
  return (
    <div style={{
      position: "sticky", top: 0, zIndex: 10,
      background: "color-mix(in srgb, var(--bg) 92%, transparent)",
      backdropFilter: "blur(12px)",
      WebkitBackdropFilter: "blur(12px)",
      padding: "16px 22px 12px",
      display: "flex", alignItems: "center", justifyContent: "space-between",
      borderBottom: "1px solid var(--rule)",
      ...style,
    }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12, minWidth: 0 }}>
        {onBack && (
          <button onClick={onBack} className="press" aria-label="Back" style={{
            width: 36, height: 36, marginLeft: -8,
            display: "inline-flex", alignItems: "center", justifyContent: "center",
            color: "var(--ink)",
          }}>
            <svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round"><path d="M12 3L4.5 9 12 15"/><path d="M4.5 9H16"/></svg>
          </button>
        )}
        <div style={{ minWidth: 0 }}>
          {eyebrow && <div style={{ marginBottom: 2 }}><Mono>{eyebrow}</Mono></div>}
          {title && <h1 style={{ fontFamily: "Newsreader, serif", fontWeight: 400, fontSize: 22, letterSpacing: "-0.01em", lineHeight: 1, color: "var(--ink)" }}>{title}</h1>}
        </div>
      </div>
      <div style={{ display: "flex", alignItems: "center", gap: 6 }}>{right}</div>
    </div>
  );
}

// ── Bottom Tab Bar ─────────────────────────────────────────────────────
const TAB_DEFS = [
  { id: "today", label: "Today" },
  { id: "book",  label: "Book"  },
  { id: "saved", label: "Saved" },
  { id: "me",    label: "Me"    },
];

function TabIcon({ id, active }) {
  const stroke = active ? "var(--ink)" : "var(--ink-soft)";
  const fill = "none";
  const sw = 1.4;
  if (id === "today") return (
    <svg width="22" height="22" viewBox="0 0 22 22" fill={fill} stroke={stroke} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
      <circle cx="11" cy="11" r={active ? 4 : 3.2} fill={active ? "var(--primary)" : "none"} stroke={active ? "var(--primary)" : stroke} />
      <circle cx="11" cy="11" r="9" />
    </svg>
  );
  if (id === "book") return (
    <svg width="22" height="22" viewBox="0 0 22 22" fill={fill} stroke={stroke} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
      <rect x="3" y="5" width="16" height="14" rx="0.5"/>
      <path d="M3 9h16"/>
      <path d="M7 3v4M15 3v4"/>
      {active && <circle cx="11" cy="14" r="1.6" fill="var(--primary)" stroke="var(--primary)"/>}
    </svg>
  );
  if (id === "saved") return (
    <svg width="22" height="22" viewBox="0 0 22 22" fill={active ? "var(--primary)" : fill} stroke={active ? "var(--primary)" : stroke} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
      <path d="M5 3h12v16l-6-4-6 4z"/>
    </svg>
  );
  if (id === "me") return (
    <svg width="22" height="22" viewBox="0 0 22 22" fill={fill} stroke={stroke} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
      <circle cx="11" cy="8" r="3.5" fill={active ? "var(--primary)" : "none"} stroke={active ? "var(--primary)" : stroke} />
      <path d="M3.5 19c1.5-3.5 4.5-5 7.5-5s6 1.5 7.5 5"/>
    </svg>
  );
  return null;
}

function TabBar({ tab, setTab, badges = {} }) {
  return (
    <nav aria-label="Primary" style={{
      position: "absolute", left: 0, right: 0, bottom: 0,
      paddingBottom: "calc(8px + var(--safe-bot))",
      paddingTop: 8,
      background: "color-mix(in srgb, var(--bg) 95%, transparent)",
      backdropFilter: "blur(16px)",
      WebkitBackdropFilter: "blur(16px)",
      borderTop: "1px solid var(--rule)",
      display: "grid", gridTemplateColumns: "repeat(4, 1fr)",
      zIndex: 20,
    }}>
      {TAB_DEFS.map((t) => {
        const active = t.id === tab;
        return (
          <button key={t.id} onClick={() => setTab(t.id)} className="press" style={{
            display: "flex", flexDirection: "column", alignItems: "center", gap: 4,
            paddingTop: 8, paddingBottom: 4,
            color: active ? "var(--ink)" : "var(--ink-soft)",
            position: "relative",
          }}>
            <TabIcon id={t.id} active={active} />
            <span style={{
              fontFamily: "'JetBrains Mono', monospace",
              fontSize: 10, letterSpacing: "0.1em",
              textTransform: "uppercase",
              fontWeight: active ? 500 : 400,
            }}>{t.label}</span>
            {badges[t.id] > 0 && (
              <span style={{
                position: "absolute", top: 4, right: "calc(50% - 22px)",
                minWidth: 16, height: 16, padding: "0 4px", borderRadius: 8,
                background: "var(--primary)", color: "var(--primary-ink)",
                fontFamily: "'JetBrains Mono', monospace", fontSize: 9,
                display: "inline-flex", alignItems: "center", justifyContent: "center",
              }}>{badges[t.id]}</span>
            )}
          </button>
        );
      })}
    </nav>
  );
}

// ── Bottom Sheet ───────────────────────────────────────────────────────
function Sheet({ open, onClose, children, peek = false, maxHeight = "86%" }) {
  const [mounted, setMounted] = useState(open);
  const [closing, setClosing] = useState(false);
  useEffect(() => {
    if (open) { setMounted(true); setClosing(false); }
    else if (mounted) {
      setClosing(true);
      const t = setTimeout(() => { setMounted(false); setClosing(false); }, 220);
      return () => clearTimeout(t);
    }
  }, [open]);
  if (!mounted) return null;
  return (
    <div style={{
      position: "absolute", inset: 0, zIndex: 60,
      display: "flex", alignItems: "flex-end", justifyContent: "stretch",
    }}>
      <div onClick={onClose} style={{
        position: "absolute", inset: 0,
        background: "rgba(15,10,7,0.42)",
        animation: closing ? "nadi-fade-bg 0.18s ease-in reverse" : "nadi-fade-bg 0.22s ease-out",
      }} />
      <div style={{
        position: "relative", zIndex: 1,
        width: "100%", maxHeight,
        background: "var(--bg)",
        borderTopLeftRadius: 24, borderTopRightRadius: 24,
        boxShadow: "0 -20px 60px rgba(15,10,7,0.35)",
        animation: closing ? "nadi-sheet-up 0.22s ease-in reverse" : "nadi-sheet-up 0.32s cubic-bezier(0.2,0.7,0.2,1)",
        overflow: "hidden",
        display: "flex", flexDirection: "column",
      }}>
        <div style={{ display: "flex", justifyContent: "center", padding: "10px 0 4px", flexShrink: 0 }}>
          <span style={{ width: 36, height: 4, borderRadius: 2, background: "var(--rule-strong)" }} />
        </div>
        <div className="scroll" style={{ flex: 1, overflow: "auto" }}>{children}</div>
      </div>
    </div>
  );
}

// ── Full-screen overlay (for confirmations) ───────────────────────────
function FullCover({ open, children, onClose }) {
  const [mounted, setMounted] = useState(open);
  const [closing, setClosing] = useState(false);
  useEffect(() => {
    if (open) { setMounted(true); setClosing(false); }
    else if (mounted) {
      setClosing(true);
      const t = setTimeout(() => { setMounted(false); setClosing(false); }, 220);
      return () => clearTimeout(t);
    }
  }, [open]);
  if (!mounted) return null;
  return (
    <div style={{
      position: "absolute", inset: 0, zIndex: 80,
      background: "var(--bg)",
      animation: closing ? "nadi-fade-bg 0.18s reverse" : "nadi-fade-bg 0.24s",
      display: "flex", flexDirection: "column",
    }}>{children}</div>
  );
}

// ── Toast ─────────────────────────────────────────────────────────────
function Toast({ msg }) {
  if (!msg) return null;
  return (
    <div style={{
      position: "absolute", left: 16, right: 16, bottom: "calc(96px + var(--safe-bot))",
      zIndex: 200,
      background: "var(--ink)", color: "var(--bg)",
      padding: "14px 18px",
      fontFamily: "Newsreader, serif", fontSize: 15,
      letterSpacing: "0.01em",
      boxShadow: "0 12px 30px rgba(20,12,8,0.35)",
      borderRadius: 4,
      animation: "nadi-fade-in 0.3s ease-out",
      display: "flex", alignItems: "center", gap: 12,
    }}>
      <span className="breath" style={{ width: 8, height: 8, background: "var(--primary)" }} />
      <span style={{ flex: 1 }}>{msg}</span>
    </div>
  );
}

// ── Inline form field ─────────────────────────────────────────────────
function Field({ label, value, onChange, placeholder, type = "text", style }) {
  return (
    <label style={{ display: "block", ...style }}>
      <Mono>{label}</Mono>
      <input type={type} value={value} onChange={(e) => onChange(e.target.value)} placeholder={placeholder}
        style={{
          marginTop: 4, width: "100%", display: "block",
          borderBottom: "1px solid var(--rule)", padding: "10px 0",
          fontFamily: "Newsreader, serif", fontSize: 20, color: "var(--ink)",
          borderRadius: 0,
        }}
      />
    </label>
  );
}

// ── Pill/Chip ─────────────────────────────────────────────────────────
function Chip({ children, active, onClick, color }) {
  return (
    <button onClick={onClick} className="press" style={{
      padding: "8px 14px",
      border: `1px solid ${active ? "var(--ink)" : "var(--rule)"}`,
      background: active ? "var(--ink)" : "transparent",
      color: active ? "var(--bg)" : (color || "var(--ink-soft)"),
      fontFamily: "'JetBrains Mono', monospace",
      fontSize: 11, letterSpacing: "0.06em",
      textTransform: "lowercase",
      borderRadius: 999,
      whiteSpace: "nowrap",
      flexShrink: 0,
    }}>{children}</button>
  );
}

// ── Stretchy headline that animates on mount ──────────────────────────
function StretchHeadline({ children, size = 56, style }) {
  return (
    <h1 className="display stretch-in" style={{
      fontSize: size, letterSpacing: "-0.025em", lineHeight: 0.95,
      color: "var(--ink)", ...style,
    }}>{children}</h1>
  );
}

// ── Tile : the universal cream card with optional accent left rail ────
function Tile({ children, accent, surface = "var(--bg)", border = "var(--rule)", style, onClick }) {
  const Comp = onClick ? "button" : "div";
  return (
    <Comp onClick={onClick} className={onClick ? "press" : ""} style={{
      width: "100%", textAlign: "left", display: "block",
      background: surface,
      border: `1px solid ${border}`,
      borderLeft: accent ? `3px solid ${accent}` : `1px solid ${border}`,
      padding: 18,
      borderRadius: 0,
      ...style,
    }}>{children}</Comp>
  );
}

// ── Status bar (mock — fakes phone status bar at very top) ───────────
function StatusBar() {
  return (
    <div style={{
      display: "flex", justifyContent: "space-between", alignItems: "center",
      padding: "8px 20px 0",
      fontFamily: "'JetBrains Mono', monospace",
      fontSize: 11, letterSpacing: "0.04em",
      color: "var(--ink)",
      fontWeight: 500,
    }}>
      <span>14:08</span>
      <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
        <span style={{ display: "inline-flex", gap: 2 }}>
          <span style={{ width: 3, height: 5, background: "currentColor", borderRadius: 0.5 }} />
          <span style={{ width: 3, height: 7, background: "currentColor", borderRadius: 0.5 }} />
          <span style={{ width: 3, height: 9, background: "currentColor", borderRadius: 0.5 }} />
          <span style={{ width: 3, height: 11, background: "currentColor", borderRadius: 0.5 }} />
        </span>
        <span style={{ marginLeft: 2 }}>5G</span>
        <span style={{
          marginLeft: 4, width: 22, height: 10, border: "1px solid currentColor", borderRadius: 2,
          position: "relative",
        }}>
          <span style={{ position: "absolute", inset: 1, width: 14, background: "currentColor" }} />
          <span style={{ position: "absolute", right: -3, top: 2, width: 2, height: 4, background: "currentColor" }} />
        </span>
      </span>
    </div>
  );
}

function PullToRefresh({ onRefresh, children }) {
  const { useState, useRef, useEffect } = React;
  const [pullProgress, setPullProgress] = useState(0); // 0 to 100
  const [refreshing, setRefreshing] = useState(false);
  const containerRef = useRef(null);
  const touchStart = useRef(0);
  const isAtTop = useRef(true);

  useEffect(() => {
    const el = containerRef.current;
    if (!el) return;

    const handleTouchStart = (e) => {
      isAtTop.current = el.scrollTop === 0;
      if (isAtTop.current) {
        touchStart.current = e.touches[0].clientY;
      }
    };

    const handleTouchMove = (e) => {
      if (!isAtTop.current) return;
      const currentY = e.touches[0].clientY;
      const dy = currentY - touchStart.current;
      if (dy > 0) {
        // Dragging down at the top
        const pull = Math.min(100, dy * 0.4);
        setPullProgress(pull);
        if (e.cancelable) e.preventDefault();
      }
    };

    const handleTouchEnd = async () => {
      if (!isAtTop.current) return;
      if (pullProgress > 60 && !refreshing) {
        setRefreshing(true);
        setPullProgress(100);
        try {
          if (onRefresh) {
            await onRefresh();
          } else if (window.NADI_REFRESH_DATA) {
            await window.NADI_REFRESH_DATA();
          }
        } catch (e) {
          console.error(e);
        } finally {
          setRefreshing(false);
          setPullProgress(0);
        }
      } else {
        setPullProgress(0);
      }
    };

    el.addEventListener("touchstart", handleTouchStart, { passive: true });
    el.addEventListener("touchmove", handleTouchMove, { passive: false });
    el.addEventListener("touchend", handleTouchEnd, { passive: true });

    return () => {
      el.removeEventListener("touchstart", handleTouchStart);
      el.removeEventListener("touchmove", handleTouchMove);
      el.removeEventListener("touchend", handleTouchEnd);
    };
  }, [pullProgress, refreshing, onRefresh]);

  const height = refreshing ? 50 : pullProgress * 0.5;

  return (
    <div ref={containerRef} className="scroll screen-fade" style={{ flex: 1, display: "flex", flexDirection: "column", position: "relative" }}>
      <div style={{
        height: `${height}px`,
        opacity: refreshing ? 1 : pullProgress / 100,
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        background: "var(--surface)",
        borderBottom: height > 0 ? "1px solid var(--rule)" : "none",
        transition: refreshing ? "height 0.2s ease, opacity 0.2s ease" : "none",
        overflow: "hidden",
        flexShrink: 0
      }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <span className="breath" style={{ width: 8, height: 8, animationDuration: refreshing ? "1.5s" : "8s" }} />
          <Mono style={{ textTransform: "lowercase", color: "var(--primary)", fontSize: 11 }}>
            {refreshing ? "refreshing..." : (pullProgress > 60 ? "release to refresh" : "pull to refresh")}
          </Mono>
        </div>
      </div>
      <div style={{ flex: 1, display: "flex", flexDirection: "column" }}>
        {children}
      </div>
    </div>
  );
}

Object.assign(window, {
  Logo, Mono, Eyebrow, Button, Avatar, FriendStack, BreathDot,
  TopBar, TabBar, TAB_DEFS, Sheet, FullCover, Toast, Field, Chip,
  StretchHeadline, Tile, StatusBar, PullToRefresh,
});
