/* global React, lucide */
// =======================================================================
// BlazeConnector Admin v3 — UI primitives
// StatusBadge, KpiCard, DataTable, FilterBar, JsonViewer, EmptyState,
// Modal, Drawer, ConfirmDialog, Channel/Tenant avatars
// =======================================================================
const { useState, useEffect, useRef, useMemo, useCallback, useLayoutEffect } = React;

/* ---------------- Lucide icon helper -------------------------------- */
function Icon({ name, size = 14, className = '', style }) {
  const ref = useRef(null);
  useLayoutEffect(() => {
    if (ref.current && window.lucide) {
      ref.current.setAttribute('data-lucide', name);
      ref.current.innerHTML = '';
      try { window.lucide.createIcons({ icons: window.lucide.icons, attrs: {}, nameAttr: 'data-lucide' }); } catch (e) {}
    }
  }, [name]);
  return <i ref={ref} data-lucide={name} style={{ width: size, height: size, display: 'inline-flex', flex: '0 0 ' + size + 'px', ...(style || {}) }} className={className}></i>;
}

/* ---------------- Channel / tenant avatars ------------------------- */
function ChannelChip({ name, size = 'md' }) {
  const cls = 'ch ' + (size === 'sm' ? 'sm' : size === 'lg' ? 'lg' : '') + ' ch-' + name;
  return <span className={cls} title={window.CHANNEL_LABEL[name] || name}>{window.CHANNEL_LETTER[name] || '·'}</span>;
}

function TenantAvatar({ tenant, size = 20 }) {
  if (!tenant) return null;
  const palette = ['av-1', 'av-2', 'av-3', 'av-4', 'av-5', 'av-6', 'av-7'];
  const cls = palette[(tenant.id || '').charCodeAt(4) % palette.length];
  const initials = tenant.name.split(' ').slice(0, 2).map(s => s[0]).join('').toUpperCase();
  return <span className={'av sq ' + cls} style={{ width: size, height: size, fontSize: Math.max(9, size * 0.42) }}>{initials}</span>;
}

function UserAvatar({ name, size = 24 }) {
  const palette = ['av-1', 'av-2', 'av-3', 'av-4', 'av-5', 'av-6'];
  const cls = palette[(name || '').charCodeAt(0) % palette.length];
  const initials = (name || '?').split(' ').slice(0, 2).map(s => s[0]).join('').toUpperCase();
  return <span className={'av ' + cls} style={{ width: size, height: size, fontSize: Math.max(9, size * 0.42) }}>{initials}</span>;
}

/* ---------------- StatusBadge --------------------------------------- */
const STATUS_MAP = {
  active: 'success', healthy: 'success', delivered: 'success', read: 'success', sent: 'info', queued: 'warn', pending: 'warn',
  failed: 'danger', error: 'danger', down: 'danger', critical: 'danger',
  expired: 'neutral', inactive: 'neutral', suspended: 'neutral', revoked: 'neutral', disabled: 'neutral',
  warning: 'warn', degraded: 'warn', info: 'info', processing: 'info', running: 'info',
  approved: 'success', rejected: 'danger', synced: 'info'
};
function StatusBadge({ status, tone, label, square = false }) {
  const t = tone || STATUS_MAP[(status || '').toLowerCase()] || 'neutral';
  return (
    <span className={'sb ' + t + (square ? ' sb-square' : '')}>
      {!square && <span className="sb-dot"></span>}
      {label || status}
    </span>
  );
}

/* ---------------- Card / SectionCard ------------------------------- */
function SectionCard({ title, sub, icon, actions, children, flush = false, className = '' }) {
  return (
    <section className={'card ' + className}>
      {(title || actions) && (
        <header className="card-head">
          <div>
            <div className="card-title">
              {icon && <Icon name={icon} />}
              <span>{title}</span>
              {sub && <span className="card-sub" style={{ marginLeft: 8 }}>{sub}</span>}
            </div>
          </div>
          {actions && <div className="card-actions">{actions}</div>}
        </header>
      )}
      <div className={flush ? 'card-body-flush' : 'card-body'}>{children}</div>
    </section>
  );
}

/* ---------------- KpiCard ------------------------------------------ */
function fmtNum(n) {
  if (n == null) return '—';
  if (Math.abs(n) >= 1e6) return (n / 1e6).toFixed(1) + 'M';
  if (Math.abs(n) >= 1e4) return (n / 1e3).toFixed(1) + 'k';
  return n.toLocaleString('en-US');
}

function KpiCard({ label, value, unit, delta, deltaTone, series, foot, color = 'orange', icon }) {
  return (
    <div className="kpi">
      <div className="kpi-head">
        <span className="kpi-label">{icon && <Icon name={icon} size={11} style={{ marginRight: 4, verticalAlign: 'middle' }} />}{label}</span>
        {delta != null && (
          <span className={'kpi-delta ' + (deltaTone || (delta > 0 ? 'up' : delta < 0 ? 'down' : 'neutral'))}>
            {delta > 0 ? '↑' : delta < 0 ? '↓' : '·'}{Math.abs(delta)}%
          </span>
        )}
      </div>
      <div className="kpi-value-row">
        <span className="kpi-value">{typeof value === 'number' ? fmtNum(value) : value}</span>
        {unit && <span className="kpi-unit">{unit}</span>}
      </div>
      {series && <Sparkline data={series} color={color} className="kpi-spark" />}
      {foot && <div className="kpi-foot">{foot}</div>}
    </div>
  );
}

/* ---------------- Sparkline + Area + Bar (SVG, no deps) ------------ */
const COLOR_MAP = { orange: '#FF5A1F', magenta: '#F000D8', violet: '#7A3CFF', blue: '#1E7BFF', green: '#2ECC5A', warn: '#F2A900', danger: '#E03E3E', teal: '#16C6B9' };

function Sparkline({ data, color = 'orange', strokeWidth = 1.5, className = '' }) {
  if (!data || data.length === 0) return null;
  const w = 100, h = 30, pad = 2;
  const min = Math.min(...data), max = Math.max(...data);
  const range = max - min || 1;
  const step = (w - pad * 2) / (data.length - 1 || 1);
  const pts = data.map((v, i) => [pad + i * step, h - pad - ((v - min) / range) * (h - pad * 2)]);
  const path = pts.map((p, i) => (i === 0 ? 'M' : 'L') + p[0].toFixed(2) + ',' + p[1].toFixed(2)).join(' ');
  const area = path + ' L' + (w - pad).toFixed(2) + ',' + (h - pad) + ' L' + pad + ',' + (h - pad) + ' Z';
  const c = COLOR_MAP[color] || color;
  const gradId = 'spark-' + color + '-' + Math.random().toString(36).slice(2, 7);
  return (
    <svg className={className} viewBox={'0 0 ' + w + ' ' + h} preserveAspectRatio="none" style={{ width: '100%', height: '100%', display: 'block' }}>
      <defs>
        <linearGradient id={gradId} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={c} stopOpacity="0.35" />
          <stop offset="100%" stopColor={c} stopOpacity="0.0" />
        </linearGradient>
      </defs>
      <path d={area} fill={'url(#' + gradId + ')'} />
      <path d={path} fill="none" stroke={c} strokeWidth={strokeWidth} strokeLinejoin="round" strokeLinecap="round" />
    </svg>
  );
}

function AreaChart({ data, height = 180, color = 'orange', yAxis = true, xLabels = null, gridLines = 4, formatY }) {
  if (!data || data.length === 0) return null;
  const w = 600, h = height;
  const padL = yAxis ? 32 : 6, padR = 8, padT = 8, padB = xLabels ? 22 : 8;
  const min = 0;
  const max = Math.max(...data) * 1.15 || 1;
  const range = max - min || 1;
  const innerW = w - padL - padR;
  const innerH = h - padT - padB;
  const step = innerW / (data.length - 1 || 1);
  const pts = data.map((v, i) => [padL + i * step, padT + innerH - ((v - min) / range) * innerH]);
  const path = pts.map((p, i) => (i === 0 ? 'M' : 'L') + p[0].toFixed(2) + ',' + p[1].toFixed(2)).join(' ');
  const area = path + ' L' + (padL + innerW).toFixed(2) + ',' + (padT + innerH) + ' L' + padL + ',' + (padT + innerH) + ' Z';
  const c = COLOR_MAP[color] || color;
  const gradId = 'area-' + color + '-' + Math.random().toString(36).slice(2, 7);
  const yTicks = [];
  for (let i = 0; i <= gridLines; i++) {
    const v = (max / gridLines) * i;
    const y = padT + innerH - (i / gridLines) * innerH;
    yTicks.push({ v, y });
  }
  return (
    <svg viewBox={'0 0 ' + w + ' ' + h} preserveAspectRatio="none" style={{ width: '100%', height: h, display: 'block' }}>
      <defs>
        <linearGradient id={gradId} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={c} stopOpacity="0.35" />
          <stop offset="100%" stopColor={c} stopOpacity="0.0" />
        </linearGradient>
      </defs>
      {yTicks.map((t, i) => (
        <g key={i}>
          <line x1={padL} x2={w - padR} y1={t.y} y2={t.y} stroke="var(--grid-line)" strokeWidth="1" />
          {yAxis && <text x={padL - 6} y={t.y + 3} fontSize="9" fill="var(--app-fg3)" textAnchor="end" fontFamily="var(--font-mono)">{formatY ? formatY(t.v) : fmtNum(Math.round(t.v))}</text>}
        </g>
      ))}
      <path d={area} fill={'url(#' + gradId + ')'} />
      <path d={path} fill="none" stroke={c} strokeWidth="1.5" strokeLinejoin="round" />
      {xLabels && xLabels.map((lbl, i) => {
        const x = padL + (i / (xLabels.length - 1)) * innerW;
        return <text key={i} x={x} y={h - 6} fontSize="9" fill="var(--app-fg3)" textAnchor="middle" fontFamily="var(--font-mono)">{lbl}</text>;
      })}
    </svg>
  );
}

function BarChart({ data, height = 160, color = 'blue', xLabels = null, formatY }) {
  if (!data || data.length === 0) return null;
  const w = 600, h = height;
  const padL = 32, padR = 6, padT = 8, padB = xLabels ? 22 : 6;
  const max = Math.max(...data) * 1.15 || 1;
  const innerW = w - padL - padR;
  const innerH = h - padT - padB;
  const barW = (innerW / data.length) * 0.62;
  const gap = innerW / data.length;
  const c = COLOR_MAP[color] || color;
  const yTicks = [0, 0.25, 0.5, 0.75, 1].map(p => ({ v: max * p, y: padT + innerH - p * innerH }));
  return (
    <svg viewBox={'0 0 ' + w + ' ' + h} preserveAspectRatio="none" style={{ width: '100%', height: h, display: 'block' }}>
      {yTicks.map((t, i) => (
        <g key={i}>
          <line x1={padL} x2={w - padR} y1={t.y} y2={t.y} stroke="var(--grid-line)" strokeWidth="1" />
          <text x={padL - 6} y={t.y + 3} fontSize="9" fill="var(--app-fg3)" textAnchor="end" fontFamily="var(--font-mono)">{formatY ? formatY(t.v) : fmtNum(Math.round(t.v))}</text>
        </g>
      ))}
      {data.map((v, i) => {
        const bh = (v / max) * innerH;
        const x = padL + i * gap + (gap - barW) / 2;
        const y = padT + innerH - bh;
        return <rect key={i} x={x} y={y} width={barW} height={bh} rx="2" fill={c} opacity="0.85" />;
      })}
      {xLabels && xLabels.map((lbl, i) => {
        const x = padL + (i + 0.5) * gap;
        return <text key={i} x={x} y={h - 6} fontSize="9" fill="var(--app-fg3)" textAnchor="middle" fontFamily="var(--font-mono)">{lbl}</text>;
      })}
    </svg>
  );
}

function StackedBar({ segments, height = 8 }) {
  // segments: [{ value, color, label }]
  const total = segments.reduce((a, s) => a + s.value, 0) || 1;
  return (
    <div style={{ display: 'flex', height, borderRadius: 4, overflow: 'hidden', background: 'var(--app-panel-3)' }}>
      {segments.map((s, i) => (
        <div key={i} title={s.label + ': ' + s.value} style={{ width: ((s.value / total) * 100) + '%', background: s.color || COLOR_MAP[s.tone] || 'var(--app-fg-faint)' }}></div>
      ))}
    </div>
  );
}

function Donut({ segments, size = 120, thickness = 14, centerLabel, centerSub }) {
  const r = (size - thickness) / 2;
  const cx = size / 2, cy = size / 2;
  const total = segments.reduce((a, s) => a + s.value, 0) || 1;
  const circ = 2 * Math.PI * r;
  let acc = 0;
  return (
    <svg width={size} height={size} viewBox={'0 0 ' + size + ' ' + size}>
      <circle cx={cx} cy={cy} r={r} fill="none" stroke="var(--app-panel-3)" strokeWidth={thickness} />
      {segments.map((s, i) => {
        const len = (s.value / total) * circ;
        const off = -acc;
        acc += len;
        return <circle key={i} cx={cx} cy={cy} r={r} fill="none" stroke={s.color || COLOR_MAP[s.tone] || '#fff'} strokeWidth={thickness} strokeDasharray={len + ' ' + (circ - len)} strokeDashoffset={off} transform={'rotate(-90 ' + cx + ' ' + cy + ')'} strokeLinecap="butt" />;
      })}
      {centerLabel && <text x={cx} y={cy - 1} fill="var(--app-fg1)" fontSize="18" fontWeight="800" textAnchor="middle" fontFamily="var(--font-display)">{centerLabel}</text>}
      {centerSub && <text x={cx} y={cy + 14} fill="var(--app-fg3)" fontSize="9" textAnchor="middle" letterSpacing="0.08em">{centerSub}</text>}
    </svg>
  );
}

/* ---------------- DataTable (controlled but light) ------------------ */
function DataTable({ columns, rows, density = 'cozy', empty, onRowClick, selectedId, getRowId }) {
  if (!rows || rows.length === 0) {
    return empty || <EmptyState title="Sin resultados" sub="Ajusta los filtros para ver datos." />;
  }
  const idKey = getRowId || ((r) => r.id);
  return (
    <div className="dt-wrap">
      <table className={'dt dt-density-' + density}>
        <thead>
          <tr>
            {columns.map((c, i) => (
              <th key={i} className={(c.numeric ? 'dt-num' : '') + ' ' + (c.className || '')} style={{ width: c.width }}>
                {c.label}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {rows.map((r) => {
            const rid = idKey(r);
            return (
              <tr key={rid} className={'dt-row ' + (selectedId === rid ? 'is-selected' : '')} onClick={onRowClick ? () => onRowClick(r) : undefined}>
                {columns.map((c, i) => (
                  <td key={i} className={(c.numeric ? 'dt-num' : '') + ' ' + (c.mono ? 'dt-mono' : '') + ' ' + (c.faint ? 'dt-faint' : '') + ' ' + (c.cellClassName || '')}>
                    {c.render ? c.render(r) : r[c.key]}
                  </td>
                ))}
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

/* ---------------- FilterBar / chips -------------------------------- */
function FilterBar({ children }) {
  return <div className="fb">{children}</div>;
}
function FilterSearch({ value, onChange, placeholder = 'Buscar' }) {
  return (
    <div className="fb-search">
      <Icon name="search" size={12} style={{ color: 'var(--app-fg3)' }} />
      <input value={value} onChange={(e) => onChange(e.target.value)} placeholder={placeholder} />
      {value && <button onClick={() => onChange('')} style={{ background: 'none', border: 'none', color: 'var(--app-fg3)', cursor: 'pointer', padding: 0 }}><Icon name="x" size={12} /></button>}
    </div>
  );
}
function FilterChip({ label, value, active, onClick, icon = 'chevrons-up-down' }) {
  return (
    <button className={'fb-chip ' + (active ? 'is-active' : '')} onClick={onClick}>
      <span>{label}</span>
      {value && <span className="fb-chip-val">{value}</span>}
      <Icon name={icon} size={11} />
    </button>
  );
}

/* ---------------- JSON viewer (syntax-highlighted) ------------------ */
function JsonViewer({ data, maxHeight }) {
  const text = useMemo(() => {
    try { return JSON.stringify(data, null, 2); } catch (e) { return String(data); }
  }, [data]);
  // syntax highlight
  const highlighted = text
    .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
    .replace(/(&quot;|")([^"]+)(&quot;|")(\s*:)/g, '<span class="jv-key">"$2"</span>$4')
    .replace(/:\s*(&quot;|")([^"]*?)(&quot;|")/g, ': <span class="jv-str">"$2"</span>')
    .replace(/:\s*(-?\d+\.?\d*)/g, ': <span class="jv-num">$1</span>')
    .replace(/:\s*(true|false)/g, ': <span class="jv-bool">$1</span>')
    .replace(/:\s*null/g, ': <span class="jv-null">null</span>');
  return <div className="jv" style={maxHeight ? { maxHeight } : undefined} dangerouslySetInnerHTML={{ __html: highlighted }}></div>;
}

/* ---------------- Empty state -------------------------------------- */
function EmptyState({ icon = 'inbox', title, sub, action }) {
  return (
    <div className="empty">
      <Icon name={icon} size={36} className="empty-ico" />
      <div className="empty-title">{title}</div>
      {sub && <div className="empty-sub">{sub}</div>}
      {action && <div style={{ marginTop: 6 }}>{action}</div>}
    </div>
  );
}

/* ---------------- Modal + Drawer + Confirm -------------------------- */
function Modal({ title, sub, onClose, children, actions }) {
  useEffect(() => {
    const fn = (e) => { if (e.key === 'Escape') onClose && onClose(); };
    window.addEventListener('keydown', fn);
    return () => window.removeEventListener('keydown', fn);
  }, [onClose]);
  return (
    <div className="modal-mask" onClick={onClose}>
      <div className="modal" onClick={(e) => e.stopPropagation()}>
        <header className="modal-head">
          <h2 className="modal-title">{title}</h2>
          {sub && <p className="modal-sub">{sub}</p>}
        </header>
        <div className="modal-body">{children}</div>
        {actions && <footer className="modal-actions">{actions}</footer>}
      </div>
    </div>
  );
}

function Drawer({ title, sub, onClose, children, actions, width }) {
  useEffect(() => {
    const fn = (e) => { if (e.key === 'Escape') onClose && onClose(); };
    window.addEventListener('keydown', fn);
    return () => window.removeEventListener('keydown', fn);
  }, [onClose]);
  return (
    <React.Fragment>
      <div className="drawer-mask" onClick={onClose}></div>
      <aside className="drawer" style={width ? { width } : undefined}>
        <header className="drawer-head">
          <div style={{ minWidth: 0, flex: 1 }}>
            <div className="drawer-title">{title}</div>
            {sub && <div className="drawer-sub">{sub}</div>}
          </div>
          <button className="btn btn-ghost btn-sm btn-icon-only drawer-close" onClick={onClose}><Icon name="x" size={14} /></button>
        </header>
        <div className="drawer-body">{children}</div>
        {actions && <footer className="drawer-actions">{actions}</footer>}
      </aside>
    </React.Fragment>
  );
}

function ConfirmDialog({ title, message, danger = false, confirmLabel = 'Confirmar', onConfirm, onClose }) {
  return (
    <Modal title={title} sub={message} onClose={onClose} actions={
      <>
        <button className="btn btn-ghost" onClick={onClose}>Cancelar</button>
        <button className={'btn ' + (danger ? 'btn-primary' : 'btn-primary')} style={danger ? { background: 'var(--danger)', borderColor: 'var(--danger)' } : undefined} onClick={() => { onConfirm && onConfirm(); onClose && onClose(); }}>{confirmLabel}</button>
      </>
    }>
    </Modal>
  );
}

/* ---------------- Misc helpers ------------------------------------- */
function timeAgo(iso) {
  const d = (Date.now() - new Date(iso).getTime()) / 1000;
  if (d < 5) return 'ahora';
  if (d < 60) return Math.floor(d) + 's';
  if (d < 3600) return Math.floor(d / 60) + 'm';
  if (d < 86400) return Math.floor(d / 3600) + 'h';
  if (d < 86400 * 7) return Math.floor(d / 86400) + 'd';
  return new Date(iso).toLocaleDateString('es-DO');
}
function fmtTime(iso) {
  const d = new Date(iso);
  return d.toLocaleTimeString('es-DO', { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false });
}
function fmtDateTime(iso) {
  const d = new Date(iso);
  return d.toLocaleDateString('es-DO', { day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit', hour12: false });
}
function fmtFullTime(iso) {
  const d = new Date(iso);
  return d.toISOString().replace('T', ' ').slice(0, 23) + 'Z';
}

Object.assign(window, {
  Icon, ChannelChip, TenantAvatar, UserAvatar,
  StatusBadge, SectionCard, KpiCard, Sparkline, AreaChart, BarChart, StackedBar, Donut,
  DataTable, FilterBar, FilterSearch, FilterChip, JsonViewer, EmptyState,
  Modal, Drawer, ConfirmDialog,
  fmtNum, timeAgo, fmtTime, fmtDateTime, fmtFullTime, COLOR_MAP
});
