// Shared UI primitives
// Expose React hooks globally so every <script type="text/babel"> can use them
// without re-destructuring. Babel-standalone evaluates each script tag in an
// isolated eval scope, so top-level consts wouldn't otherwise be reachable.
window.useState = React.useState;
window.useEffect = React.useEffect;
window.useRef = React.useRef;
window.useMemo = React.useMemo;
window.createContext = React.createContext;
window.useContext = React.useContext;
const { useState, useEffect, useRef, useMemo, createContext, useContext } = React;

// ----- Toast system -----
const ToastCtx = createContext(null);

function ToastProvider({ children }) {
  const [items, setItems] = useState([]);
  const push = (toast) => {
    const id = Math.random().toString(36).slice(2);
    setItems((t) => [...t, { id, ...toast }]);
    setTimeout(() => setItems((t) => t.filter((x) => x.id !== id)), toast.duration || 3500);
  };
  return (
    <ToastCtx.Provider value={push}>
      {children}
      <div className="toasts">
        {items.map((t) => (
          <div key={t.id} className={"toast " + (t.kind || "")}>
            <i className={"toast-icon fas " + (t.icon || (
              t.kind === "success" ? "fa-check-circle" :
              t.kind === "error" ? "fa-exclamation-circle" :
              t.kind === "info" ? "fa-info-circle" : "fa-bell"
            ))}></i>
            <div className="toast-body">
              <div className="toast-title">{t.title}</div>
              {t.msg && <div className="toast-msg">{t.msg}</div>}
            </div>
          </div>
        ))}
      </div>
    </ToastCtx.Provider>
  );
}
const useToast = () => useContext(ToastCtx);

// ----- Confirm dialog (substitui window.confirm com identidade do sistema) -----
const ConfirmCtx = createContext(null);

function ConfirmProvider({ children }) {
  const [state, setState] = useState(null);   // { opts, resolve } | null
  const resolverRef = useRef(null);

  const ask = (opts) => new Promise((resolve) => {
    resolverRef.current = resolve;
    setState({
      opts: Object.assign({
        title: 'Confirmar ação',
        message: 'Tem certeza?',
        confirmLabel: 'Confirmar',
        cancelLabel: 'Cancelar',
        danger: false,
        icon: null,
        details: null,
      }, opts || {}),
    });
  });

  const close = (result) => {
    const r = resolverRef.current;
    resolverRef.current = null;
    setState(null);
    if (r) r(result);
  };

  // Esc fecha como cancel, Enter como confirm
  useEffect(() => {
    if (!state) return;
    const onKey = (e) => {
      if (e.key === 'Escape') { e.preventDefault(); close(false); }
      if (e.key === 'Enter')  { e.preventDefault(); close(true); }
    };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [state]);

  return (
    <ConfirmCtx.Provider value={ask}>
      {children}
      {state && (
        <div className="modal-overlay" onClick={() => close(false)}>
          <div className="confirm-dialog" onClick={(e) => e.stopPropagation()} role="alertdialog">
            <div className="confirm-icon-wrap">
              <div className={"confirm-icon " + (state.opts.danger ? 'danger' : 'info')}>
                <i className={"fas " + (state.opts.icon || (state.opts.danger ? 'fa-trash-alt' : 'fa-question-circle'))}></i>
              </div>
            </div>
            <div className="confirm-body">
              <h3 className="confirm-title">{state.opts.title}</h3>
              <div className="confirm-message">{state.opts.message}</div>
              {state.opts.details && (
                <div className="confirm-details">{state.opts.details}</div>
              )}
            </div>
            <div className="confirm-actions">
              <button
                type="button"
                className="btn btn-ghost"
                onClick={() => close(false)}
                autoFocus
              >
                {state.opts.cancelLabel}
              </button>
              <button
                type="button"
                className={"btn " + (state.opts.danger ? 'btn-danger-solid' : 'btn-primary')}
                onClick={() => close(true)}
              >
                <i className={"fas " + (state.opts.danger ? 'fa-trash' : 'fa-check')}></i> {state.opts.confirmLabel}
              </button>
            </div>
          </div>
        </div>
      )}
    </ConfirmCtx.Provider>
  );
}
const useConfirm = () => useContext(ConfirmCtx);

// ----- Range slider -----
function Range({ min = 0, max = 1, step = 0.1, value, onChange, format }) {
  const pct = ((value - min) / (max - min)) * 100;
  const style = { "--pct": pct + "%" };
  return (
    <div className="range" style={style}>
      <div className="range-head">
        <span>{format ? format(min) : min}</span>
        <span className="val">{format ? format(value) : value}</span>
        <span>{format ? format(max) : max}</span>
      </div>
      <input
        type="range"
        min={min}
        max={max}
        step={step}
        value={value}
        onChange={(e) => onChange(parseFloat(e.target.value))}
      />
    </div>
  );
}

// ----- Switch -----
function Switch({ on, onChange, label }) {
  return (
    <label className="switch-row" onClick={() => onChange(!on)}>
      <span className={"switch" + (on ? " on" : "")}></span>
      {label && <span>{label}</span>}
    </label>
  );
}

// ----- Status badge -----
function StatusBadge({ status }) {
  const map = {
    indexed:    { cls: "b-gr", icon: "fa-check-circle", label: "Indexado" },
    completed:  { cls: "b-gr", icon: "fa-check-circle", label: "Indexado" },
    processing: { cls: "b-am", icon: "fa-spinner fa-spin", label: "Processando" },
    pending:    { cls: "b-mut",icon: "fa-clock", label: "Pendente" },
    error:      { cls: "b-rd", icon: "fa-exclamation-circle", label: "Erro" },
    failed:     { cls: "b-rd", icon: "fa-exclamation-triangle", label: "Falhou" },
    online:     { cls: "b-gr", icon: "fa-circle", label: "Online" },
    enabled:    { cls: "b-gr", icon: "fa-circle", label: "Ativo" },
    disabled:   { cls: "b-mut",icon: "fa-circle", label: "Inativo" },
  };
  const m = map[status] || map.disabled;
  return (
    <span className={"badge " + m.cls}>
      <i className={"fas " + m.icon}></i>
      {m.label}
    </span>
  );
}

// ----- Save state pill -----
function SaveState({ state }) {
  if (state === "saving") return <div className="save-state saving"><i className="fas fa-circle-notch fa-spin"></i> Salvando…</div>;
  if (state === "saved")  return <div className="save-state saved"><i className="fas fa-check"></i> Tudo salvo</div>;
  if (state === "dirty")  return <div className="save-state dirty"><i className="fas fa-circle"></i> Alterações não salvas</div>;
  return null;
}

// ----- Area Chart (SVG) -----
function AreaChart({ data, height = 180, accent = "var(--or)", secondary = "var(--rd)" }) {
  if (!data || !data.length) return null;
  const W = 800, H = height, PAD_L = 40, PAD_R = 16, PAD_T = 16, PAD_B = 28;
  const innerW = W - PAD_L - PAD_R;
  const innerH = H - PAD_T - PAD_B;

  const maxV = Math.max(...data.map((d) => d.v)) * 1.15;
  const stepX = innerW / Math.max(1, data.length - 1);

  const pts = data.map((d, i) => [PAD_L + i * stepX, PAD_T + innerH - (d.v / maxV) * innerH]);
  const fbPts = data.map((d, i) => [PAD_L + i * stepX, PAD_T + innerH - (d.fb / maxV) * innerH]);

  const line = pts.map((p, i) => (i === 0 ? "M" : "L") + p[0] + "," + p[1]).join(" ");
  const area = line + " L" + pts[pts.length - 1][0] + "," + (PAD_T + innerH) + " L" + pts[0][0] + "," + (PAD_T + innerH) + " Z";
  const fbLine = fbPts.map((p, i) => (i === 0 ? "M" : "L") + p[0] + "," + p[1]).join(" ");

  // Y ticks
  const ticks = 4;
  const tickVals = Array.from({ length: ticks + 1 }, (_, i) => Math.round((maxV / ticks) * i / 10) * 10);

  return (
    <svg viewBox={`0 0 ${W} ${H}`} style={{ width: "100%", height: "auto", display: "block" }}>
      <defs>
        <linearGradient id="areaG" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={accent} stopOpacity="0.35" />
          <stop offset="100%" stopColor={accent} stopOpacity="0" />
        </linearGradient>
      </defs>

      {/* Y grid + labels */}
      {tickVals.map((tv, i) => {
        const y = PAD_T + innerH - (tv / maxV) * innerH;
        return (
          <g key={i}>
            <line x1={PAD_L} y1={y} x2={W - PAD_R} y2={y} stroke="var(--b1)" strokeDasharray="2 4" />
            <text x={PAD_L - 8} y={y + 3} fill="var(--t3)" fontSize="10" textAnchor="end" fontFamily="var(--ff-mono)">{tv}</text>
          </g>
        );
      })}

      {/* X labels */}
      {data.map((d, i) => {
        if (i % 2 !== 0 && i !== data.length - 1) return null;
        const x = PAD_L + i * stepX;
        return <text key={i} x={x} y={H - 8} fill="var(--t3)" fontSize="10" textAnchor="middle" fontFamily="var(--ff-mono)">{d.d}</text>;
      })}

      {/* Area + line */}
      <path d={area} fill="url(#areaG)" />
      <path d={line} stroke={accent} strokeWidth="2" fill="none" strokeLinejoin="round" />

      {/* Fallback line */}
      <path d={fbLine} stroke={secondary} strokeWidth="1.5" strokeDasharray="3 3" fill="none" opacity="0.7" />

      {/* Dots on main */}
      {pts.map((p, i) => (
        <circle key={i} cx={p[0]} cy={p[1]} r={i === pts.length - 1 ? 4 : 0} fill={accent} stroke="var(--bg2)" strokeWidth="2" />
      ))}
    </svg>
  );
}

// ----- Sparkline -----
function Sparkline({ data, color = "var(--or)", height = 38 }) {
  if (!data || !data.length) return null;
  const W = 200, H = height;
  const min = Math.min(...data);
  const max = Math.max(...data);
  const range = max - min || 1;
  const stepX = W / (data.length - 1 || 1);
  const pts = data.map((v, i) => [i * stepX, H - ((v - min) / range) * (H - 4) - 2]);
  const line = pts.map((p, i) => (i === 0 ? "M" : "L") + p[0] + "," + p[1]).join(" ");
  const area = line + ` L${W},${H} L0,${H} Z`;
  return (
    <svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none" className="spark">
      <defs>
        <linearGradient id="sparkG" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={color} stopOpacity="0.35" />
          <stop offset="100%" stopColor={color} stopOpacity="0" />
        </linearGradient>
      </defs>
      <path d={area} fill="url(#sparkG)" />
      <path d={line} stroke={color} strokeWidth="1.5" fill="none" />
    </svg>
  );
}

// Expose to global
Object.assign(window, {
  ToastProvider, useToast, ToastCtx,
  ConfirmProvider, useConfirm, ConfirmCtx,
  Range, Switch, StatusBadge, SaveState, AreaChart, Sparkline,
});
