// Hospital Dashboard — reusable components
// Vitals waveforms, KPI cards, status badges, toasts, etc.

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

// ─── Smooth animated number ─────────────────────────────
function Ticker({ value, decimals = 0, duration = 600, prefix = '', suffix = '', className }) {
  const [display, setDisplay] = useState(value);
  const fromRef = useRef(value);
  const startRef = useRef(0);
  const rafRef = useRef(0);

  useEffect(() => {
    fromRef.current = display;
    startRef.current = performance.now();
    const tick = (now) => {
      const t = Math.min(1, (now - startRef.current) / duration);
      const eased = 1 - Math.pow(1 - t, 3); // easeOutCubic
      const v = fromRef.current + (value - fromRef.current) * eased;
      setDisplay(v);
      if (t < 1) rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(rafRef.current);
  }, [value, duration]);

  return <span className={className}>{prefix}{display.toFixed(decimals)}{suffix}</span>;
}

// ─── Live ECG waveform — scrolling ──────────────────────
function ECGWave({ bpm = 80, color = '#10b981', height = 60, width = '100%', running = true }) {
  const ref = useRef(null);
  const offsetRef = useRef(0);

  useEffect(() => {
    const canvas = ref.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    const dpr = window.devicePixelRatio || 1;
    let w = canvas.clientWidth, h = canvas.clientHeight;
    if (w === 0 || h === 0) return;
    canvas.width = w * dpr; canvas.height = h * dpr;
    ctx.scale(dpr, dpr);

    const samplesPerSec = 200;
    const beatsPerSec = bpm / 60;
    const wave = window.ecgWaveform(Math.floor(samplesPerSec * 4), Math.max(1, Math.round(beatsPerSec * 4)), 1, 0.04);
    const speed = 1.4;

    const drawFrame = () => {
      ctx.clearRect(0, 0, w, h);
      // Subtle grid
      ctx.strokeStyle = 'rgba(120, 120, 140, 0.08)';
      ctx.lineWidth = 1;
      for (let x = 0; x < w; x += 12) {
        ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, h); ctx.stroke();
      }
      for (let y = 0; y < h; y += 12) {
        ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(w, y); ctx.stroke();
      }

      // Waveform
      ctx.lineWidth = 1.8;
      ctx.strokeStyle = color;
      ctx.shadowColor = color; ctx.shadowBlur = 6;
      ctx.beginPath();
      for (let x = 0; x < w; x++) {
        const idx = Math.floor(offsetRef.current + x) % wave.length;
        const v = wave[idx];
        const y = h * 0.55 - v * h * 0.35;
        if (x === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
      }
      ctx.stroke();
      ctx.shadowBlur = 0;

      // Leading dot
      const idx = Math.floor(offsetRef.current + w - 1) % wave.length;
      const v = wave[idx];
      ctx.fillStyle = color;
      ctx.beginPath();
      ctx.arc(w - 2, h * 0.55 - v * h * 0.35, 2.2, 0, Math.PI * 2);
      ctx.fill();
    };

    // Paint one frame immediately (visible even when rAF is paused)
    drawFrame();

    if (!running) return;
    let raf;
    const tick = () => { drawFrame(); offsetRef.current += speed; raf = requestAnimationFrame(tick); };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [bpm, color, running]);

  return <canvas ref={ref} style={{ width, height, display: 'block' }} />;
}

// ─── Mini sparkline ─────────────────────────────────────
function Sparkline({ data, color = '#6366f1', height = 32, width = 96, fill = true }) {
  if (!data || data.length === 0) return null;
  const min = Math.min(...data), max = Math.max(...data);
  const range = max - min || 1;
  const step = width / (data.length - 1);
  const pts = data.map((v, i) => `${i * step},${height - ((v - min) / range) * (height - 4) - 2}`);
  const linePath = `M ${pts.join(' L ')}`;
  const fillPath = `${linePath} L ${width},${height} L 0,${height} Z`;
  return (
    <svg width={width} height={height} style={{ display: 'block', overflow: 'visible' }}>
      {fill && <path d={fillPath} fill={color} opacity="0.12" />}
      <path d={linePath} stroke={color} strokeWidth="1.5" fill="none" strokeLinejoin="round" strokeLinecap="round"/>
    </svg>
  );
}

// ─── Status pill ────────────────────────────────────────
function StatusPill({ status, children, dot = true, size = 'md' }) {
  const map = {
    alert:   { bg: 'rgba(239,68,68,0.15)',  fg: 'var(--h-alert)',   dot: 'var(--h-alert)' },
    critical:{ bg: 'rgba(239,68,68,0.15)',  fg: 'var(--h-alert)',   dot: 'var(--h-alert)' },
    warning: { bg: 'rgba(245,158,11,0.15)', fg: 'var(--h-warning)', dot: 'var(--h-warning)' },
    watch:   { bg: 'rgba(245,158,11,0.15)', fg: 'var(--h-warning)', dot: 'var(--h-warning)' },
    stable:  { bg: 'rgba(34,197,94,0.15)',  fg: 'var(--h-success)', dot: 'var(--h-success)' },
    online:  { bg: 'rgba(34,197,94,0.15)',  fg: 'var(--h-success)', dot: 'var(--h-success)' },
    idle:    { bg: 'rgba(148,163,184,0.18)',fg: 'var(--h-muted)',   dot: 'var(--h-muted)' },
    offline: { bg: 'rgba(148,163,184,0.18)',fg: 'var(--h-muted)',   dot: 'var(--h-muted)' },
    info:    { bg: 'rgba(99,102,241,0.15)', fg: 'var(--h-primary)', dot: 'var(--h-primary)' },
  };
  const s = map[status] || map.info;
  const padding = size === 'sm' ? '2px 8px' : '4px 10px';
  const fs = size === 'sm' ? 10 : 11;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 6,
      padding, background: s.bg, color: s.fg,
      borderRadius: 999, fontSize: fs, fontWeight: 600, letterSpacing: '0.02em', textTransform: 'capitalize',
      whiteSpace: 'nowrap',
    }}>
      {dot && <span style={{
        width: 6, height: 6, borderRadius: '50%', background: s.dot,
        boxShadow: status === 'alert' || status === 'critical' ? `0 0 8px ${s.dot}` : 'none',
        animation: status === 'alert' || status === 'critical' ? 'pulse 1.2s ease-in-out infinite' : 'none',
      }}/>}
      {children || status}
    </span>
  );
}

// ─── KPI card ────────────────────────────────────────────
function KPICard({ label, value, delta, icon, color = 'primary', spark, suffix = '' }) {
  const colors = {
    primary: 'var(--h-primary)',
    alert: 'var(--h-alert)',
    warning: 'var(--h-warning)',
    success: 'var(--h-success)',
  };
  const c = colors[color];
  return (
    <div className="kpi-card card-surface" style={{ padding: '20px 22px' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 12 }}>
        <div style={{ width: 36, height: 36, borderRadius: 10, background: `${c}1F`, color: c, display: 'grid', placeItems: 'center' }}>
          {icon}
        </div>
        {delta !== undefined && (
          <span style={{ fontSize: 11, fontWeight: 600, color: delta >= 0 ? 'var(--h-success)' : 'var(--h-alert)', display: 'inline-flex', alignItems: 'center', gap: 3 }}>
            {delta >= 0 ? '↑' : '↓'} {Math.abs(delta)}%
          </span>
        )}
      </div>
      <div style={{ fontSize: 12, color: 'var(--h-muted)', textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600, marginBottom: 4 }}>{label}</div>
      <div style={{ fontSize: 32, fontWeight: 700, letterSpacing: '-0.02em', color: 'var(--h-text)', lineHeight: 1 }}>
        <Ticker value={value} decimals={0} suffix={suffix} />
      </div>
      {spark && <div style={{ marginTop: 12 }}><Sparkline data={spark} color={c} width={140} height={28} /></div>}
    </div>
  );
}

// ─── Toast / notification stack ─────────────────────────
const ToastContext = React.createContext(null);

function ToastProvider({ children }) {
  const [toasts, setToasts] = useState([]);
  const push = useCallback((t) => {
    const id = Math.random().toString(36).slice(2);
    setToasts(prev => [...prev, { id, ...t }]);
    if (t.duration !== 0) setTimeout(() => setToasts(prev => prev.filter(x => x.id !== id)), t.duration || 4500);
  }, []);
  const dismiss = useCallback((id) => setToasts(prev => prev.filter(x => x.id !== id)), []);

  return (
    <ToastContext.Provider value={{ push, dismiss }}>
      {children}
      <div className="toast-stack">
        {toasts.map(t => (
          <div key={t.id} className={`toast toast-${t.sev || 'info'}`}>
            <div className="toast-icon">
              {t.sev === 'alert' ? '⚠' : t.sev === 'success' ? '✓' : 'ℹ'}
            </div>
            <div style={{ flex: 1 }}>
              <div style={{ fontWeight: 600, fontSize: 13 }}>{t.title}</div>
              {t.body && <div style={{ fontSize: 12, opacity: 0.75, marginTop: 2 }}>{t.body}</div>}
            </div>
            <button onClick={() => dismiss(t.id)} className="toast-x">×</button>
          </div>
        ))}
      </div>
    </ToastContext.Provider>
  );
}

function useToast() { return React.useContext(ToastContext); }

// ─── Glyph icons (clean stroke) ─────────────────────────
const Icon = {
  Heart: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 1 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  Activity: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><path d="M22 12h-4l-3 9L9 3l-3 9H2" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  Users: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  Building: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><path d="M3 21h18M5 21V7l8-4v18M19 21V11l-6-4M9 9h.01M9 13h.01M9 17h.01" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  Grid: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><rect x="3" y="3" width="7" height="7" stroke="currentColor" strokeWidth="1.8" rx="1.5"/><rect x="14" y="3" width="7" height="7" stroke="currentColor" strokeWidth="1.8" rx="1.5"/><rect x="3" y="14" width="7" height="7" stroke="currentColor" strokeWidth="1.8" rx="1.5"/><rect x="14" y="14" width="7" height="7" stroke="currentColor" strokeWidth="1.8" rx="1.5"/></svg>,
  Bell: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><path d="M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9M13.73 21a2 2 0 0 1-3.46 0" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  Device: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><rect x="5" y="3" width="14" height="18" rx="2" stroke="currentColor" strokeWidth="1.8"/><path d="M12 18h.01" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/></svg>,
  Map: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><path d="M1 6v16l7-3 8 3 7-3V3l-7 3-8-3-7 3z M8 3v15M16 6v15" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  Search: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" {...p}><circle cx="11" cy="11" r="8" stroke="currentColor" strokeWidth="2"/><path d="m21 21-4.35-4.35" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/></svg>,
  Plus: (p) => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" {...p}><path d="M12 5v14M5 12h14" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round"/></svg>,
  Sun: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><circle cx="12" cy="12" r="4" stroke="currentColor" strokeWidth="1.8"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"/></svg>,
  Moon: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  ChevronRight: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" {...p}><path d="m9 18 6-6-6-6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  ChevronLeft: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" {...p}><path d="m15 18-6-6 6-6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  X: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" {...p}><path d="M18 6 6 18M6 6l12 12" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/></svg>,
  Logout: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  Filter: (p) => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" {...p}><path d="M22 3H2l8 9.46V19l4 2v-8.54L22 3z" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round"/></svg>,
  Drop: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><path d="M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5S5 13 5 15a7 7 0 0 0 7 7z" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round"/></svg>,
  Therm: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><path d="M14 14.76V3.5a2.5 2.5 0 0 0-5 0v11.26a4.5 4.5 0 1 0 5 0z" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round"/></svg>,
  Wind: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><path d="M9.59 4.59A2 2 0 1 1 11 8H2M12.59 19.41A2 2 0 1 0 14 16H2M17.73 7.73A2.5 2.5 0 1 1 19.5 12H2" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>,
};

Object.assign(window, {
  Ticker, ECGWave, Sparkline, StatusPill, KPICard,
  ToastProvider, useToast, ToastContext, Icon,
});
