// Device Management — components

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

// ─── Ticker ────────────────────────────────────────────
function DMTicker({ value, decimals = 0, duration = 700, 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);
      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.toLocaleString(undefined, { minimumFractionDigits: decimals, maximumFractionDigits: decimals })}{suffix}</span>;
}

// ─── Sparkline ─────────────────────────────────────────
function DMSpark({ data, color = '#00d4ff', width = 100, height = 36 }) {
  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 ')}`;
  return (
    <svg width={width} height={height} style={{ display: 'block', overflow: 'visible' }}>
      <path d={linePath} stroke={color} strokeWidth="1.6" fill="none" strokeLinejoin="round" strokeLinecap="round"/>
      <circle cx={(data.length - 1) * step} cy={height - ((data[data.length-1] - min) / range) * (height - 4) - 2} r="2.5" fill={color}/>
    </svg>
  );
}

// ─── Donut chart for release distribution ─────────────
function DMDonut({ data, total, size = 180, thickness = 22 }) {
  const cx = size / 2, cy = size / 2;
  const r = (size - thickness) / 2;
  const circ = 2 * Math.PI * r;
  let cumulative = 0;
  return (
    <svg width={size} height={size}>
      <circle cx={cx} cy={cy} r={r} fill="none" stroke="rgba(255,255,255,0.05)" strokeWidth={thickness}/>
      {data.map((d, i) => {
        const pct = d.count / total;
        const dash = pct * circ;
        const gap = circ - dash;
        const offset = -cumulative * circ;
        cumulative += pct;
        return (
          <circle key={i} cx={cx} cy={cy} r={r} fill="none"
            stroke={d.color} strokeWidth={thickness}
            strokeDasharray={`${dash} ${gap}`}
            strokeDashoffset={offset}
            transform={`rotate(-90 ${cx} ${cy})`}
            style={{ transition: 'stroke-dasharray 0.8s cubic-bezier(0.2, 0.8, 0.2, 1)' }}/>
        );
      })}
      <text x={cx} y={cy - 4} textAnchor="middle" fill="var(--dm-text)" fontSize="28" fontWeight="700" fontFamily="JetBrains Mono">
        <DMTicker value={total} />
      </text>
      <text x={cx} y={cy + 16} textAnchor="middle" fill="var(--dm-muted)" fontSize="10" letterSpacing="0.1em">DEVICES</text>
    </svg>
  );
}

// ─── Health Progress Bar ───────────────────────────────
function HealthBar({ value, status = 'auto', height = 6 }) {
  const color = status === 'auto' ? (value >= 80 ? 'var(--dm-success)' : value >= 60 ? 'var(--dm-warning)' : 'var(--dm-alert)')
              : status === 'healthy' ? 'var(--dm-success)' : status === 'warning' ? 'var(--dm-warning)' : 'var(--dm-alert)';
  return (
    <div className="dm-hbar" style={{ height }}>
      <div className="dm-hbar-fill" style={{ width: `${value}%`, background: color, '--bar-color': color }}/>
    </div>
  );
}

// ─── Status pill ───────────────────────────────────────
function DMStatusPill({ status, size = 'md' }) {
  const map = {
    healthy:    { bg: 'var(--dm-success-bg)', fg: 'var(--dm-success)', label: 'Healthy' },
    warning:    { bg: 'var(--dm-warning-bg)', fg: 'var(--dm-warning)', label: 'Warning' },
    critical:   { bg: 'var(--dm-alert-bg)',   fg: 'var(--dm-alert)',   label: 'Critical' },
    offline:    { bg: 'rgba(148,163,184,0.15)', fg: 'var(--dm-muted)', label: 'Offline' },
    in_progress:{ bg: 'var(--dm-primary-soft)', fg: 'var(--dm-primary)', label: 'Running' },
    completed:  { bg: 'var(--dm-success-bg)', fg: 'var(--dm-success)', label: 'Done' },
    failed:     { bg: 'var(--dm-alert-bg)',   fg: 'var(--dm-alert)',   label: 'Failed' },
  };
  const s = map[status] || map.offline;
  return (
    <span className={`dm-status dm-status-${status} dm-status-${size}`} style={{ background: s.bg, color: s.fg }}>
      <span className="dm-status-dot" style={{ background: s.fg }}/>
      {s.label}
    </span>
  );
}

// ─── Toast ──────────────────────────────────────────────
const DMToastContext = React.createContext(null);
function DMToastProvider({ 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 (
    <DMToastContext.Provider value={{ push, dismiss }}>
      {children}
      <div className="dm-toast-stack">
        {toasts.map(t => (
          <div key={t.id} className={`dm-toast dm-toast-${t.sev || 'info'}`}>
            <div className="dm-toast-icon">{t.sev === 'success' ? '✓' : t.sev === 'alert' ? '!' : 'i'}</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="dm-toast-x">×</button>
          </div>
        ))}
      </div>
    </DMToastContext.Provider>
  );
}
function useDMToast() { return React.useContext(DMToastContext); }

// ─── Live health metrics chart (line) ──────────────────
function DMHealthChart({ height = 220, live = true }) {
  const ref = useRef(null);
  const dataRef = useRef({
    cpu: [...window.DM_HEALTH_HISTORY.cpu],
    memory: [...window.DM_HEALTH_HISTORY.memory],
    disk: [...window.DM_HEALTH_HISTORY.disk],
    queue: [...window.DM_HEALTH_HISTORY.queue],
  });
  const [, force] = useState(0);

  useEffect(() => {
    if (!live) return;
    const id = setInterval(() => {
      const d = dataRef.current;
      const next = (arr, base, range) => {
        const last = arr[arr.length - 1];
        const target = base + (Math.random() - 0.5) * range;
        const val = Math.max(0, Math.min(100, last + (target - last) * 0.3));
        return [...arr.slice(1), val];
      };
      d.cpu = next(d.cpu, 38, 30);
      d.memory = next(d.memory, 54, 24);
      d.disk = next(d.disk, 40, 8);
      d.queue = next(d.queue, 14, 16);
      force(x => x + 1);
    }, 1200);
    return () => clearInterval(id);
  }, [live]);

  const d = dataRef.current;
  const [dim, setDim] = useState({ w: 600, h: height });
  useEffect(() => {
    const update = () => { if (ref.current) setDim({ w: ref.current.clientWidth, h: height }); };
    update(); window.addEventListener('resize', update);
    return () => window.removeEventListener('resize', update);
  }, [height]);

  const padL = 40, padR = 20, padT = 16, padB = 24;
  const w = dim.w - padL - padR;
  const h = dim.h - padT - padB;
  const buildPath = (arr) => {
    const step = w / (arr.length - 1);
    return 'M ' + arr.map((v, i) => `${padL + i * step},${padT + h - (v / 100) * h}`).join(' L ');
  };
  const series = [
    { name: 'CPU',    data: d.cpu,    color: '#00d4ff' },
    { name: 'Memory', data: d.memory, color: '#67e8f9' },
    { name: 'Disk',   data: d.disk,   color: '#fbbf24' },
    { name: 'Queue',  data: d.queue,  color: '#f87171' },
  ];

  return (
    <div ref={ref} style={{ width: '100%' }}>
      <div className="dm-chart-legend">
        {series.map(s => (
          <div key={s.name} className="dm-cl-item">
            <span style={{ width: 8, height: 8, borderRadius: '50%', background: s.color }}/>
            <span>{s.name}</span>
            <span className="dm-cl-val mono">{s.data[s.data.length - 1].toFixed(1)}%</span>
          </div>
        ))}
      </div>
      <svg width={dim.w} height={dim.h} style={{ display: 'block', overflow: 'visible' }}>
        {/* Grid */}
        {[0, 25, 50, 75, 100].map((v, i) => (
          <g key={i}>
            <line x1={padL} y1={padT + h - (v / 100) * h} x2={padL + w} y2={padT + h - (v / 100) * h}
              stroke="rgba(255,255,255,0.05)" strokeDasharray="3,4"/>
            <text x={padL - 6} y={padT + h - (v / 100) * h + 4} fill="var(--dm-muted)" fontSize="9.5" textAnchor="end" fontFamily="JetBrains Mono">{v}%</text>
          </g>
        ))}
        {series.map(s => (
          <path key={s.name} d={buildPath(s.data)} stroke={s.color} strokeWidth="2" fill="none"
            strokeLinejoin="round" strokeLinecap="round"
            filter={`drop-shadow(0 0 4px ${s.color}55)`}/>
        ))}
      </svg>
    </div>
  );
}

// ─── Icons (mission-control set) ─────────────────────────
const DMIcon = {
  Fleet: (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.6" rx="1.5"/><rect x="14" y="3" width="7" height="7" stroke="currentColor" strokeWidth="1.6" rx="1.5"/><rect x="3" y="14" width="7" height="7" stroke="currentColor" strokeWidth="1.6" rx="1.5"/><rect x="14" y="14" width="7" height="7" stroke="currentColor" strokeWidth="1.6" rx="1.5"/></svg>,
  OTA: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><path d="M12 2v8M12 22V14m-9-2h6m6 0h6M5.6 5.6l4.2 4.2m4.4 4.4 4.2 4.2M5.6 18.4l4.2-4.2m4.4-4.4 4.2-4.2" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/><circle cx="12" cy="12" r="2.5" stroke="currentColor" strokeWidth="1.6"/></svg>,
  Release: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><rect x="3" y="4" width="18" height="16" rx="2" stroke="currentColor" strokeWidth="1.6"/><path d="M8 9h8M8 13h5M8 17h3" stroke="currentColor" strokeWidth="1.6" strokeLinecap="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 8z" stroke="currentColor" strokeWidth="1.6"/></svg>,
  Alert: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0zM12 9v4M12 17h.01" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  Settings: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><circle cx="12" cy="12" r="3" stroke="currentColor" strokeWidth="1.6"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" stroke="currentColor" strokeWidth="1.6"/></svg>,
  Topology: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" {...p}><circle cx="6" cy="6" r="3" stroke="currentColor" strokeWidth="1.6"/><circle cx="18" cy="6" r="3" stroke="currentColor" strokeWidth="1.6"/><circle cx="12" cy="18" r="3" stroke="currentColor" strokeWidth="1.6"/><path d="m8.5 7.5 7 1m-6 6 6-6m-7 8 7 0" stroke="currentColor" strokeWidth="1.6" strokeLinecap="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="1.8"/><path d="m21 21-4.35-4.35" stroke="currentColor" strokeWidth="1.8" 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"/></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>,
  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>,
  Refresh: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" {...p}><path d="M3 12a9 9 0 0 1 15-6.7L21 8M21 3v5h-5M21 12a9 9 0 0 1-15 6.7L3 16M3 21v-5h5" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  Power: (p) => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" {...p}><path d="M18.36 6.64a9 9 0 1 1-12.72 0M12 2v10" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/></svg>,
  Download: (p) => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" {...p}><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3" stroke="currentColor" strokeWidth="1.8" 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>,
  Check: (p) => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" {...p}><path d="m20 6-11 11-5-5" stroke="currentColor" strokeWidth="2.4" 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>,
  ChevronRight: (p) => <svg width="14" height="14" 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>,
  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>,
  Cpu: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" {...p}><rect x="4" y="4" width="16" height="16" rx="2" stroke="currentColor" strokeWidth="1.6"/><rect x="9" y="9" width="6" height="6" stroke="currentColor" strokeWidth="1.6"/><path d="M9 1v3M15 1v3M9 20v3M15 20v3M1 9h3M1 15h3M20 9h3M20 15h3" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/></svg>,
  Memory: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" {...p}><path d="M2 9h20v6H2zM6 9v6M10 9v6M14 9v6M18 9v6" stroke="currentColor" strokeWidth="1.6"/></svg>,
  Disk: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" {...p}><ellipse cx="12" cy="5" rx="9" ry="3" stroke="currentColor" strokeWidth="1.6"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3M21 5v14c0 1.66-4 3-9 3s-9-1.34-9-3V5" stroke="currentColor" strokeWidth="1.6"/></svg>,
  Wifi: (p) => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" {...p}><path d="M1.42 9a16 16 0 0 1 21.16 0M5 12.55a11 11 0 0 1 14.08 0M8.53 16.11a6 6 0 0 1 6.95 0M12 20h.01" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>,
};

Object.assign(window, {
  DMTicker, DMSpark, DMDonut, HealthBar, DMStatusPill,
  DMToastProvider, useDMToast, DMToastContext, DMHealthChart, DMIcon,
});
