// ============================================
// Shared components
// ============================================
const { useState, useEffect, useRef, useMemo } = React;

// Button
function Button({ children, variant = "primary", size, full, icon, iconRight, onClick, type, className = "", ...rest }) {
  const sizeCls = size === "sm" ? "btn-sm" : size === "lg" ? "btn-lg" : "";
  return (
    <button type={type || "button"} onClick={onClick} className={`btn btn-${variant} ${sizeCls} ${full ? "btn-full" : ""} ${className}`} {...rest}>
      {icon && <Icon name={icon} size={16} />}
      {children}
      {iconRight && <Icon name={iconRight} size={16} />}
    </button>
  );
}

// Card
function Card({ children, accent, style, className = "", onClick }) {
  const accentStyle = accent ? { borderLeft: `4px solid ${accent}` } : {};
  return (
    <div className={`card ${className}`} style={{ ...accentStyle, ...style }} onClick={onClick}>{children}</div>
  );
}

// Pill
function Pill({ children, tone = "default", icon }) {
  const cls = { default: "pill", aqua: "pill pill-aqua", mint: "pill pill-mint", amber: "pill pill-amber", red: "pill pill-red", navy: "pill pill-navy" }[tone] || "pill";
  return <span className={cls}>{icon && <Icon name={icon} size={12} />}{children}</span>;
}

// Avatar
function Avatar({ name = "", src, size = "md", color }) {
  const initials = name.split(" ").map(n => n[0]).slice(0, 2).join("").toUpperCase();
  const cls = size === "sm" ? "avatar avatar-sm" : size === "lg" ? "avatar avatar-lg" : "avatar";
  const style = color ? { background: color } : {};
  if (src) return <img className={cls} src={src} alt={name} />;
  return <div className={cls} style={style}>{initials || "•"}</div>;
}

// Stat Card (with mono number + accent border)
function StatCard({ label, value, sub, accent = "var(--aqua)", icon, trend }) {
  return (
    <div className="card" style={{ borderLeft: `4px solid ${accent}`, padding: 18 }}>
      <div className="between">
        <div className="t-label" style={{ color: "var(--gray-600)" }}>{label}</div>
        {icon && <Icon name={icon} size={18} color="var(--gray-500)" />}
      </div>
      <div className="t-mono-stat" style={{ marginTop: 8, color: "var(--gray-900)" }}>{value}</div>
      {sub && (
        <div className="row" style={{ marginTop: 6, fontSize: 12, color: trend === "up" ? "var(--mint)" : trend === "down" ? "var(--red)" : "var(--gray-600)" }}>
          {trend === "up" && <Icon name="arrow-up" size={12} />}
          {trend === "down" && <Icon name="arrow-down" size={12} />}
          <span>{sub}</span>
        </div>
      )}
    </div>
  );
}

// Progress Ring
function ProgressRing({ pct = 0, size = 80, stroke = 8, color = "var(--aqua)", label, sublabel }) {
  const r = (size - stroke) / 2;
  const c = 2 * Math.PI * r;
  const offset = c - (pct / 100) * c;
  return (
    <div style={{ width: size, height: size, position: "relative", display: "inline-block" }}>
      <svg width={size} height={size} style={{ transform: "rotate(-90deg)" }}>
        <circle cx={size/2} cy={size/2} r={r} stroke="var(--gray-200)" strokeWidth={stroke} fill="none" />
        <circle cx={size/2} cy={size/2} r={r} stroke={color} strokeWidth={stroke} fill="none"
          strokeDasharray={c} strokeDashoffset={offset} strokeLinecap="round"
          style={{ transition: "stroke-dashoffset 600ms ease-in-out" }} />
      </svg>
      <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }}>
        <div className="t-mono-sm" style={{ fontWeight: 700, fontSize: size * 0.22 }}>{label ?? `${pct}%`}</div>
        {sublabel && <div style={{ fontSize: 10, color: "var(--gray-600)", marginTop: 2 }}>{sublabel}</div>}
      </div>
    </div>
  );
}

// Progress Bar
function ProgressBar({ pct = 0, color = "var(--aqua)", height = 6 }) {
  return (
    <div style={{ height, background: "var(--gray-200)", borderRadius: 999, overflow: "hidden" }}>
      <div style={{ width: `${Math.max(0, Math.min(100, pct))}%`, height: "100%", background: color, borderRadius: 999, transition: "width 400ms ease-out" }} />
    </div>
  );
}

// Sparkline
function Sparkline({ values, color = "var(--aqua)", height = 32, width = 120, fill = true }) {
  if (!values?.length) return null;
  const min = Math.min(...values), max = Math.max(...values);
  const range = max - min || 1;
  const pts = values.map((v, i) => [i * (width / (values.length - 1)), height - ((v - min) / range) * (height - 4) - 2]);
  const d = "M " + pts.map(p => p.join(",")).join(" L ");
  const fillD = d + ` L ${width},${height} L 0,${height} Z`;
  return (
    <svg width={width} height={height} style={{ display: "block" }}>
      {fill && <path d={fillD} fill={color} opacity={0.12} />}
      <path d={d} fill="none" stroke={color} strokeWidth={2} strokeLinejoin="round" strokeLinecap="round" />
    </svg>
  );
}

// Bar chart
function BarChart({ data, color = "var(--aqua)", height = 120, max }) {
  const m = max || Math.max(...data.map(d => d.value));
  return (
    <div style={{ display: "grid", gridTemplateColumns: `repeat(${data.length}, 1fr)`, gap: 6, alignItems: "end", height }}>
      {data.map((d, i) => (
        <div key={i} style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 6, height: "100%", justifyContent: "flex-end" }}>
          <div style={{ width: "70%", height: `${(d.value / m) * 88}%`, background: d.color || color, borderRadius: 4, transition: "height 600ms" }} />
          <div style={{ fontSize: 10, color: "var(--gray-600)" }}>{d.label}</div>
        </div>
      ))}
    </div>
  );
}

// Line chart (responsive svg, single series)
function LineChart({ values, labels, color = "var(--aqua)", height = 140 }) {
  const ref = useRef(null);
  const [w, setW] = useState(400);
  useEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver(e => setW(e[0].contentRect.width));
    ro.observe(ref.current);
    return () => ro.disconnect();
  }, []);
  const padL = 28, padR = 8, padT = 8, padB = 22;
  const cw = Math.max(120, w - padL - padR), ch = height - padT - padB;
  const min = Math.min(...values), max = Math.max(...values), range = max - min || 1;
  const pts = values.map((v, i) => [padL + i * (cw / (values.length - 1)), padT + ch - ((v - min) / range) * ch]);
  const d = "M " + pts.map(p => p.join(",")).join(" L ");
  const fillD = d + ` L ${pts[pts.length-1][0]},${padT + ch} L ${padL},${padT + ch} Z`;
  return (
    <div ref={ref} style={{ width: "100%" }}>
      <svg width="100%" height={height}>
        <defs>
          <linearGradient id="lg-fill" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor={color} stopOpacity={0.25}/>
            <stop offset="100%" stopColor={color} stopOpacity={0}/>
          </linearGradient>
        </defs>
        {[0, 0.25, 0.5, 0.75, 1].map(p => (
          <line key={p} x1={padL} x2={padL + cw} y1={padT + ch * p} y2={padT + ch * p} stroke="var(--gray-200)" strokeDasharray="2 4"/>
        ))}
        <path d={fillD} fill="url(#lg-fill)"/>
        <path d={d} stroke={color} strokeWidth={2.5} fill="none" strokeLinejoin="round" strokeLinecap="round"/>
        {pts.map(([x, y], i) => (
          <circle key={i} cx={x} cy={y} r={3} fill="white" stroke={color} strokeWidth={2}/>
        ))}
        {labels && labels.map((l, i) => (
          <text key={i} x={pts[i][0]} y={height - 6} fontSize="10" fill="var(--gray-600)" textAnchor="middle" fontFamily="var(--f-body)">{l}</text>
        ))}
      </svg>
    </div>
  );
}

// Toast
function ToastHost({ toast }) {
  if (!toast) return null;
  const tone = toast.tone || "success";
  const bg = tone === "success" ? "var(--mint)" : tone === "error" ? "var(--red)" : tone === "warning" ? "var(--amber)" : "var(--navy)";
  return (
    <div style={{ position: "fixed", bottom: 84, left: "50%", transform: "translateX(-50%)", background: bg, color: tone === "success" ? "var(--navy-900)" : "white", padding: "12px 20px", borderRadius: 999, boxShadow: "var(--sh-8)", zIndex: 1000, animation: "fade-up 220ms ease-out" }}>
      <div className="row">
        <Icon name={tone === "success" ? "check-circle" : tone === "error" ? "alert" : "info"} size={18} />
        <span style={{ fontWeight: 600 }}>{toast.message}</span>
      </div>
    </div>
  );
}

// Bottom Sheet / Modal
function Sheet({ open, onClose, children, title, large }) {
  if (!open) return null;
  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(10,15,30,0.6)", zIndex: 800, display: "flex", alignItems: "flex-end", justifyContent: "center", animation: "fade-up 200ms ease-out" }}>
      <div onClick={e => e.stopPropagation()} style={{ background: "white", width: "100%", maxWidth: large ? 720 : 520, maxHeight: "85vh", borderRadius: "20px 20px 0 0", padding: 0, animation: "fade-up 260ms cubic-bezier(.16,1,.3,1)", overflow: "hidden", display: "flex", flexDirection: "column" }}>
        <div style={{ height: 5, width: 40, background: "var(--gray-300)", borderRadius: 999, margin: "10px auto 0" }} />
        <div className="between" style={{ padding: "16px 20px 8px" }}>
          <div className="t-h2">{title}</div>
          <button className="btn-icon" onClick={onClose}><Icon name="x" size={18}/></button>
        </div>
        <div style={{ padding: "8px 20px 24px", overflow: "auto" }}>{children}</div>
      </div>
    </div>
  );
}

// Confetti burst
function Confetti({ count = 50 }) {
  const colors = ["var(--mint)", "var(--gold)", "var(--aqua)", "var(--navy)"];
  const bits = Array.from({ length: count }).map((_, i) => ({
    left: Math.random() * 100,
    delay: Math.random() * 0.4,
    dur: 1.2 + Math.random() * 0.8,
    color: colors[i % colors.length],
    size: 6 + Math.random() * 8,
  }));
  return (
    <div style={{ position: "fixed", inset: 0, pointerEvents: "none", zIndex: 999 }}>
      {bits.map((b, i) => (
        <div key={i} style={{
          position: "absolute", top: -20, left: `${b.left}%`,
          width: b.size, height: b.size * 0.4,
          background: b.color, borderRadius: 2,
          animation: `confetti ${b.dur}s ${b.delay}s ease-in forwards`,
        }} />
      ))}
    </div>
  );
}

// Empty state
function EmptyState({ icon = "info", title, sub, ctaLabel, onCta, accent = "var(--aqua)" }) {
  return (
    <div style={{ padding: "48px 24px", textAlign: "center" }}>
      <div style={{ width: 80, height: 80, borderRadius: "50%", background: "var(--sky)", display: "inline-flex", alignItems: "center", justifyContent: "center", marginBottom: 16 }}>
        <Icon name={icon} size={36} color={accent}/>
      </div>
      <div className="t-h2" style={{ marginBottom: 6 }}>{title}</div>
      {sub && <div className="muted" style={{ maxWidth: 340, margin: "0 auto" }}>{sub}</div>}
      {ctaLabel && <div style={{ marginTop: 18 }}><Button variant="primary" onClick={onCta}>{ctaLabel}</Button></div>}
    </div>
  );
}

// Toggle switch
function Toggle({ on, onChange, color = "var(--aqua)" }) {
  return (
    <button onClick={() => onChange(!on)} style={{
      width: 40, height: 22, borderRadius: 999, background: on ? color : "var(--gray-300)",
      position: "relative", transition: "background 200ms ease",
    }}>
      <span style={{ position: "absolute", top: 2, left: on ? 20 : 2, width: 18, height: 18, borderRadius: "50%", background: "white", transition: "left 200ms ease", boxShadow: "var(--sh-2)" }}/>
    </button>
  );
}

// Tabs
function Tabs({ tabs, value, onChange }) {
  return (
    <div style={{ display: "flex", gap: 6, padding: 4, background: "var(--gray-100)", borderRadius: 999, width: "fit-content", maxWidth: "100%", overflowX: "auto" }}>
      {tabs.map(t => {
        const id = typeof t === "string" ? t : t.id;
        const label = typeof t === "string" ? t : t.label;
        const active = value === id;
        return (
          <button key={id} onClick={() => onChange(id)}
            style={{
              padding: "8px 14px", borderRadius: 999, fontWeight: 600, fontSize: 13, whiteSpace: "nowrap",
              background: active ? "var(--navy)" : "transparent",
              color: active ? "white" : "var(--gray-700)",
              transition: "all 160ms",
            }}>
            {label}
          </button>
        );
      })}
    </div>
  );
}

// Header (top bar inside a portal page)
function PageHeader({ title, sub, actions, accent }) {
  return (
    <div className="between" style={{ marginBottom: 20, gap: 12, flexWrap: "wrap" }}>
      <div>
        <div className="t-h1" style={{ color: "var(--gray-900)" }}>{title}</div>
        {sub && <div className="muted" style={{ marginTop: 4 }}>{sub}</div>}
      </div>
      {actions && <div className="row" style={{ gap: 8, flexWrap: "wrap" }}>{actions}</div>}
    </div>
  );
}

// SVG Map placeholder
function MapPlaceholder({ height = 320, route = true }) {
  return (
    <div style={{ position: "relative", borderRadius: 12, overflow: "hidden", height, background: "linear-gradient(135deg, #d8e9f2, #b0d4e7)" }}>
      <svg width="100%" height="100%" viewBox="0 0 400 320" preserveAspectRatio="none">
        <defs>
          <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
            <path d="M 40 0 L 0 0 0 40" fill="none" stroke="rgba(255,255,255,0.4)" strokeWidth="1"/>
          </pattern>
        </defs>
        <rect width="400" height="320" fill="url(#grid)"/>
        <path d="M 50 280 Q 100 200 140 220 T 220 160 Q 280 120 320 60" fill="none" stroke="rgba(0,51,102,0.15)" strokeWidth="20" strokeLinecap="round"/>
        <path d="M 30 240 Q 80 220 130 160 T 280 200 Q 350 150 380 80" fill="none" stroke="rgba(0,51,102,0.1)" strokeWidth="14" strokeLinecap="round"/>
        {route && (
          <>
            <path d="M 60 260 Q 120 180 180 200 Q 240 220 280 140 Q 320 80 360 100" fill="none" stroke="var(--aqua)" strokeWidth="4" strokeLinecap="round" strokeDasharray="500" strokeDashoffset="0"/>
            <circle cx="60" cy="260" r="8" fill="white" stroke="var(--mint)" strokeWidth="3"/>
            <circle cx="360" cy="100" r="8" fill="white" stroke="var(--navy)" strokeWidth="3"/>
            <circle cx="220" cy="200" r="5" fill="var(--aqua)"/>
          </>
        )}
      </svg>
    </div>
  );
}

// Image placeholder (banded SVG)
function ImageSlot({ height = 160, label = "image", radius = 12, dark }) {
  return (
    <div style={{ position: "relative", height, borderRadius: radius, overflow: "hidden", background: dark ? "var(--navy)" : "var(--gray-100)" }}>
      <svg width="100%" height="100%" viewBox="0 0 100 60" preserveAspectRatio="none">
        <defs>
          <pattern id={`stripes-${dark ? "d" : "l"}`} width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
            <line x1="0" y1="0" x2="0" y2="6" stroke={dark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.05)"} strokeWidth="3"/>
          </pattern>
        </defs>
        <rect width="100" height="60" fill={`url(#stripes-${dark ? "d" : "l"})`}/>
      </svg>
      <div style={{ position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: "var(--f-mono)", fontSize: 11, color: dark ? "rgba(255,255,255,0.5)" : "var(--gray-500)", letterSpacing: 1, textTransform: "uppercase" }}>{label}</div>
    </div>
  );
}

Object.assign(window, { Button, Card, Pill, Avatar, StatCard, ProgressRing, ProgressBar, Sparkline, BarChart, LineChart, ToastHost, Sheet, Confetti, EmptyState, Toggle, Tabs, PageHeader, MapPlaceholder, ImageSlot });
