// components.jsx — Shared building blocks (Sparkline, MiniBar, Avatar, etc.)

// ─── Sparkline ──────────────────────────────────────────────────────────────
function Sparkline({ data, w = 80, h = 28, area = true, stroke = 'var(--accent)' }) {
  if (!data || !data.length) return null;
  const min = Math.min(...data);
  const max = Math.max(...data);
  const range = max - min || 1;
  const dx = w / (data.length - 1);
  const pts = data.map((v, i) => [i * dx, h - ((v - min) / range) * (h - 4) - 2]);
  const linePath = pts.map((p, i) => (i === 0 ? `M${p[0]},${p[1]}` : `L${p[0]},${p[1]}`)).join(' ');
  const areaPath = `${linePath} L${w},${h} L0,${h} Z`;
  return (
    <svg width={w} height={h} viewBox={`0 0 ${w} ${h}`}>
      {area && <path d={areaPath} fill={stroke} opacity="0.10" />}
      <path d={linePath} fill="none" stroke={stroke} strokeWidth="1.5" strokeLinejoin="round" strokeLinecap="round" />
    </svg>
  );
}

// ─── Mini bar chart ─────────────────────────────────────────────────────────
function MiniBars({ data, w = 80, h = 28, color = 'var(--accent)' }) {
  if (!data || !data.length) return null;
  const max = Math.max(...data) || 1;
  const bw = w / data.length;
  const gap = 1.5;
  return (
    <svg width={w} height={h} viewBox={`0 0 ${w} ${h}`}>
      {data.map((v, i) => {
        const bh = (v / max) * (h - 2);
        return <rect key={i} x={i * bw + gap / 2} y={h - bh} width={bw - gap} height={bh} fill={color} opacity={0.85} rx="0.5" />;
      })}
    </svg>
  );
}

// ─── Donut chart ────────────────────────────────────────────────────────────
function Donut({ data, size = 100, thickness = 14 }) {
  const total = data.reduce((s, d) => s + d.value, 0) || 1;
  const r = size / 2 - thickness / 2;
  const cx = size / 2, cy = size / 2;
  const C = 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(--bg-active)" strokeWidth={thickness} />
      {data.map((d, i) => {
        const len = (d.value / total) * C;
        const dash = `${len} ${C - len}`;
        const off = -acc;
        acc += len;
        return (
          <circle key={i} cx={cx} cy={cy} r={r} fill="none"
                  stroke={d.color} strokeWidth={thickness}
                  strokeDasharray={dash} strokeDashoffset={off}
                  transform={`rotate(-90 ${cx} ${cy})`} />
        );
      })}
    </svg>
  );
}

// ─── Avatar with stable color from string ───────────────────────────────────
function avatarColor(s) {
  let h = 0;
  for (let i = 0; i < (s || '').length; i++) h = (h * 31 + s.charCodeAt(i)) % 360;
  return `linear-gradient(135deg, oklch(0.68 0.14 ${h}), oklch(0.5 0.18 ${(h + 60) % 360}))`;
}
function Avatar({ name, size = 22, style }) {
  const initial = (name || '?').replace(/[^A-Za-z0-9\u4e00-\u9fa5]/g, '')[0] || '?';
  return (
    <span className="avatar-sm" style={{ width: size, height: size, fontSize: size * 0.42, background: avatarColor(name), ...style }}>
      {initial.toUpperCase()}
    </span>
  );
}

// ─── Status badge for various entity statuses ───────────────────────────────
function StatusBadge({ status, t }) {
  const map = {
    normal:      { cls: 'success', label: t.s_normal },
    live:        { cls: 'success', label: t.s_normal },
    frozen:      { cls: 'danger',  label: t.s_frozen },
    pending:     { cls: 'warning', label: t.s_pending },
    review:      { cls: 'warning', label: t.s_pending },
    approved:    { cls: 'success', label: t.s_approved },
    rejected:    { cls: 'danger',  label: t.s_rejected },
    paused:      { cls: '',        label: t.s_paused },
    draft:       { cls: 'outline', label: 'Draft' },
    maintenance: { cls: 'warning', label: t.s_maintenance },
    overdue:     { cls: 'danger',  label: t.s_overdue },
    risk:        { cls: 'danger',  label: t.s_risk },
    expired:     { cls: '',        label: t.s_expired },
    flagged:     { cls: 'warning', label: 'Flagged' },
    open:        { cls: 'warning', label: 'Open' },
    investigating: { cls: 'info', label: 'Investigating' },
    resolved:    { cls: 'success', label: 'Resolved' },
    warning:     { cls: 'warning', label: 'Warning' },
  };
  const s = map[status] || { cls: '', label: status };
  return <span className={`badge ${s.cls}`}><span className="dot" />{s.label}</span>;
}

// ─── Tier badge (for member tiers) ──────────────────────────────────────────
const TIER_NAMES_I18N = (t) => {
  const tx = (t && t.tx) ? t.tx : ((zh) => zh);
  return {
    normal:  tx('普通','Normal'),
    bronze:  tx('青铜','Bronze'),
    silver:  tx('白银','Silver'),
    gold:    tx('黄金','Gold'),
    diamond: tx('钻石','Diamond'),
    supreme: tx('至尊','Supreme'),
  };
};
function TierBadge({ tier, t }) {
  const names = TIER_NAMES_I18N(t);
  const cls = ({ normal: 'outline', bronze: 'warning', silver: 'silver', gold: 'gold', diamond: 'diamond', supreme: 'purple' })[tier] || 'outline';
  return <span className={`badge ${cls}`}>{names[tier] || tier}</span>;
}

// ─── Risk dot ───────────────────────────────────────────────────────────────
function RiskDot({ score }) {
  let color = 'var(--success)';
  if (score >= 70) color = 'var(--danger)';
  else if (score >= 40) color = 'var(--warning)';
  else if (score >= 20) color = 'var(--info)';
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontVariantNumeric: 'tabular-nums' }}>
      <span style={{ width: 7, height: 7, borderRadius: '50%', background: color }} />
      <span style={{ color: score >= 70 ? 'var(--danger)' : 'var(--text-muted)' }}>{score}</span>
    </span>
  );
}

// ─── Toast manager ──────────────────────────────────────────────────────────
function useToasts() {
  const [items, setItems] = React.useState([]);
  const push = React.useCallback((msg) => {
    const id = Math.random().toString(36).slice(2);
    setItems((arr) => [...arr, { id, msg }]);
    setTimeout(() => setItems((arr) => arr.filter((x) => x.id !== id)), 2400);
  }, []);
  const node = (
    <div className="toast-stack">
      {items.map((t) => <div key={t.id} className="toast"><Icons.check />{t.msg}</div>)}
    </div>
  );
  return [push, node];
}

// ─── Modal ──────────────────────────────────────────────────────────────────
function Modal({ open, onClose, title, width = 560, children, footer }) {
  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [open, onClose]);
  if (!open) return null;
  return (
    <div className="modal-mask" onClick={onClose}>
      <div className="modal" onClick={(e) => e.stopPropagation()} style={{ width }}>
        <div className="modal-h">
          <h2>{title}</h2>
          <button className="icon-btn" onClick={onClose}><Icons.x /></button>
        </div>
        <div className="modal-body">{children}</div>
        {footer && <div className="modal-f">{footer}</div>}
      </div>
    </div>
  );
}

// ─── Money formatter ────────────────────────────────────────────────────────
function fmt(n, sign = false, t = null) {
  if (n == null) return '—';
  const abs = Math.abs(n);
  const tx = (t && t.tx) ? t.tx : ((zh) => zh);
  let str;
  if (abs >= 1e8) str = (n / 1e8).toFixed(2) + tx(' 亿',' B');
  else if (abs >= 1e4) str = (n / 1e4).toFixed(2) + tx(' 万',' k');
  else str = Math.round(n).toLocaleString();
  if (sign && n > 0) str = '+' + str;
  return str;
}
function fmtCN(n) { return n == null ? '—' : '¥' + n.toLocaleString(undefined, { maximumFractionDigits: 2 }); }
function fmtNum(n) { return n == null ? '—' : n.toLocaleString(); }

// ─── Date range helpers ─────────────────────────────────────────────────────
function _utcISO(d) { return d.toISOString().slice(0, 10); }
function _startOfWeekUTC(d) {
  const x = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()));
  const dow = (x.getUTCDay() || 7) - 1; // Mon=0..Sun=6
  x.setUTCDate(x.getUTCDate() - dow);
  return x;
}
function dateRangePreset(key) {
  const now = new Date();
  switch (key) {
    case 'today': return [_utcISO(now), _utcISO(now)];
    case 'yesterday': {
      const d = new Date(now); d.setUTCDate(d.getUTCDate() - 1);
      return [_utcISO(d), _utcISO(d)];
    }
    case 'this_week': {
      const s = _startOfWeekUTC(now);
      return [_utcISO(s), _utcISO(now)];
    }
    case 'last_week': {
      const ls = _startOfWeekUTC(new Date(now.getTime() - 7 * 86400000));
      const le = new Date(ls); le.setUTCDate(ls.getUTCDate() + 6);
      return [_utcISO(ls), _utcISO(le)];
    }
    case 'this_month': {
      const s = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
      return [_utcISO(s), _utcISO(now)];
    }
    case 'last_month': {
      const s = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 1, 1));
      const e = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 0));
      return [_utcISO(s), _utcISO(e)];
    }
  }
  return [null, null];
}
function datePreset(key) {
  // Single-day preset: returns one ISO date string per key
  const [from] = dateRangePreset(key);
  if (key === 'this_week' || key === 'last_week') {
    // Use start of week (Mon)
    return dateRangePreset(key)[0];
  }
  if (key === 'this_month' || key === 'last_month') {
    return dateRangePreset(key)[0];
  }
  return from;
}

// ─── DateRangePicker ────────────────────────────────────────────────────────
// Unified date filter with preset chip strip: 今日 / 昨日 / 本周 / 上周 / 当月 / 上月 / 自定义
//
//   <DateRangePicker from={from} to={to} onChange={(f,t)=>...} />         // range
//   <DateRangePicker date={date} onChange={(d)=>...} mode="single" />     // single
//
// When a preset is active, inputs are read-only. Clicking 自定义 unlocks them.
function DateRangePicker({ from, to, date, onChange, mode = 'range', size, t }) {
  const tx = (t && t.tx) ? t.tx : ((zh) => zh);

  const PRESETS = [
    ['today',      tx('今日', 'Today')],
    ['yesterday',  tx('昨日', 'Yesterday')],
    ['this_week',  tx('本周', 'This Week')],
    ['last_week',  tx('上周', 'Last Week')],
    ['this_month', tx('当月', 'This Month')],
    ['last_month', tx('上月', 'Last Month')],
  ];

  // Detect which preset (if any) currently matches the values
  const activePreset = React.useMemo(() => {
    if (mode === 'single') {
      if (!date) return null;
      for (const [k] of PRESETS) {
        if (datePreset(k) === date) return k;
      }
      return null;
    }
    if (!from || !to) return null;
    for (const [k] of PRESETS) {
      const [pf, pt] = dateRangePreset(k);
      if (pf === from && pt === to) return k;
    }
    return null;
  }, [from, to, date, mode]);

  const [custom, setCustom] = React.useState(activePreset == null);
  React.useEffect(() => { setCustom(activePreset == null); }, [activePreset]);

  const pickPreset = (key) => {
    setCustom(false);
    if (mode === 'single') {
      onChange(datePreset(key));
    } else {
      const [f, tt] = dateRangePreset(key);
      onChange(f, tt);
    }
  };

  const onCustom = () => setCustom(true);

  const onFromChange = (v) => {
    if (mode === 'single') { onChange(v); return; }
    // Auto-correct end < start: bump `to` to match `from`
    const next = (to && v && v > to) ? v : to;
    onChange(v, next);
  };
  const onToChange = (v) => {
    const next = (from && v && v < from) ? from : v;
    onChange(from, next);
  };

  const readOnly = !custom;
  const fontSize = size === 'sm' ? 11 : 12;

  return (
    <div className="date-range-picker" style={{ display: 'inline-flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
      <div style={{ display: 'inline-flex', gap: 4, flexWrap: 'wrap' }}>
        {PRESETS.map(([k, label]) => (
          <span
            key={k}
            className={'chip' + (activePreset === k && !custom ? ' active' : '')}
            onClick={() => pickPreset(k)}
            style={{ cursor: 'pointer', fontSize }}
          >{label}</span>
        ))}
        <span
          className={'chip' + (custom ? ' active' : '')}
          onClick={onCustom}
          style={{ cursor: 'pointer', fontSize }}
        >{tx('自定义', 'Custom')}</span>
      </div>
      {mode === 'single' ? (
        <input
          className="input"
          type="date"
          value={date || ''}
          readOnly={readOnly}
          onChange={(e) => onFromChange(e.target.value)}
          style={{ width: 140 }}
        />
      ) : (
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4 }}>
          <input
            className="input"
            type="date"
            value={from || ''}
            readOnly={readOnly}
            onChange={(e) => onFromChange(e.target.value)}
            style={{ width: 140 }}
          />
          <span style={{ color: 'var(--text-faint)', fontSize: 11 }}>~</span>
          <input
            className="input"
            type="date"
            value={to || ''}
            readOnly={readOnly}
            onChange={(e) => onToChange(e.target.value)}
            style={{ width: 140 }}
          />
        </span>
      )}
    </div>
  );
}

Object.assign(window, {
  Sparkline, MiniBars, Donut, Avatar, StatusBadge, TierBadge, RiskDot,
  useToasts, Modal, fmt, fmtCN, fmtNum, avatarColor,
  DateRangePicker, dateRangePreset, datePreset,
});
