/* Shared UI primitives + icons */

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

/* ============================================================ Icons
   Stroke-based, 16x16 grid, 1.5 stroke. Lucide-style geometry, original drawings. */
const Icon = ({ name, size = 16, strokeWidth = 1.6, ...rest }) => {
  const paths = {
    dashboard: <><rect x="2" y="2" width="5.5" height="7" rx="1.2"/><rect x="2" y="11" width="5.5" height="3" rx="1.2"/><rect x="9.5" y="2" width="4.5" height="3" rx="1.2"/><rect x="9.5" y="7" width="4.5" height="7" rx="1.2"/></>,
    search: <><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5 14 14"/></>,
    box: <><path d="M2 5 8 2l6 3v6L8 14 2 11z"/><path d="M2 5l6 3 6-3M8 8v6"/></>,
    flask: <><path d="M6 2v3.5L3 12a1.5 1.5 0 0 0 1.4 2h7.2A1.5 1.5 0 0 0 13 12L10 5.5V2"/><path d="M5 2h6"/></>,
    layers: <><path d="M8 2 2 5l6 3 6-3z"/><path d="M2 8l6 3 6-3"/><path d="M2 11l6 3 6-3"/></>,
    bell: <><path d="M3.5 11h9l-1.2-2.2V6a3.3 3.3 0 0 0-6.6 0v2.8z"/><path d="M6 13a2 2 0 0 0 4 0"/></>,
    grid: <><rect x="2" y="2" width="5" height="5" rx="1"/><rect x="9" y="2" width="5" height="5" rx="1"/><rect x="2" y="9" width="5" height="5" rx="1"/><rect x="9" y="9" width="5" height="5" rx="1"/></>,
    truck: <><rect x="1" y="4" width="9" height="7" rx="1"/><path d="M10 6h3l2 2.5V11h-5"/><circle cx="4.5" cy="12" r="1.4"/><circle cx="12" cy="12" r="1.4"/></>,
    chart: <><path d="M2 13V3"/><path d="M2 13h12"/><path d="M5 10V8M8 10V5M11 10V7"/></>,
    spark: <><path d="M2 11l3-4 3 2 4-6 2 3"/></>,
    sparkles: <><path d="M5 2v3M3.5 3.5h3M11 9v3M9.5 10.5h3M8 5l1.6 3.4L13 10l-3.4 1.6L8 15l-1.6-3.4L3 10l3.4-1.6z"/></>,
    settings: <><circle cx="8" cy="8" r="2"/><path d="M8 1.5v2M8 12.5v2M1.5 8h2M12.5 8h2M3.5 3.5l1.4 1.4M11.1 11.1l1.4 1.4M3.5 12.5l1.4-1.4M11.1 4.9l1.4-1.4"/></>,
    calendar: <><rect x="2" y="3" width="12" height="11" rx="1.2"/><path d="M2 6h12M5 1.5v3M11 1.5v3"/></>,
    book: <><path d="M2 3a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v11a1 1 0 0 0-1-1H2z"/><path d="M14 3a1 1 0 0 0-1-1H9a1 1 0 0 0-1 1v11a1 1 0 0 1 1-1h5z"/></>,
    wrench: <><path d="m13 3-2 2 1 1-2 2-1-1-4 4a1.5 1.5 0 0 0 2 2l4-4-1-1 2-2 1 1 2-2z"/></>,
    shield: <><path d="M8 1.5 2.5 4v4c0 3 2.5 5.5 5.5 6.5 3-1 5.5-3.5 5.5-6.5V4z"/></>,
    users: <><circle cx="6" cy="6" r="2.5"/><path d="M2 13c0-2.2 1.8-4 4-4s4 1.8 4 4"/><circle cx="11" cy="7" r="2"/><path d="M14 13c0-1.7-1.3-3-3-3"/></>,
    chevronRight: <><path d="m6 3 4 5-4 5"/></>,
    chevronDown: <><path d="m3 6 5 4 5-4"/></>,
    chevronLeft: <><path d="m10 3-4 5 4 5"/></>,
    arrowUp: <><path d="M8 13V3M4 7l4-4 4 4"/></>,
    arrowDown: <><path d="M8 3v10M4 9l4 4 4-4"/></>,
    plus: <><path d="M8 3v10M3 8h10"/></>,
    minus: <><path d="M3 8h10"/></>,
    x: <><path d="M4 4l8 8M12 4l-8 8"/></>,
    check: <><path d="m3 8 3 3 7-7"/></>,
    moon: <><path d="M13 9.5A5.5 5.5 0 1 1 6.5 3a4.5 4.5 0 0 0 6.5 6.5z"/></>,
    sun: <><circle cx="8" cy="8" r="2.5"/><path d="M8 1.5v2M8 12.5v2M1.5 8h2M12.5 8h2M3.5 3.5l1.4 1.4M11.1 11.1l1.4 1.4M3.5 12.5l1.4-1.4M11.1 4.9l1.4-1.4"/></>,
    filter: <><path d="M2 3h12l-4.5 5.5V13L6.5 11.5V8.5z"/></>,
    download: <><path d="M8 2v8M4 7l4 4 4-4M2 13h12"/></>,
    upload: <><path d="M8 10V2M4 6l4-4 4 4M2 13h12"/></>,
    moreH: <><circle cx="3" cy="8" r="1"/><circle cx="8" cy="8" r="1"/><circle cx="13" cy="8" r="1"/></>,
    moreV: <><circle cx="8" cy="3" r="1"/><circle cx="8" cy="8" r="1"/><circle cx="8" cy="13" r="1"/></>,
    edit: <><path d="m11 2 3 3-8 8H3v-3z"/></>,
    trash: <><path d="M3 4h10M6 4V2.5h4V4M5 4l.5 9h5L11 4"/></>,
    copy: <><rect x="5" y="5" width="9" height="9" rx="1.2"/><path d="M11 5V3.5A1.5 1.5 0 0 0 9.5 2h-6A1.5 1.5 0 0 0 2 3.5v6A1.5 1.5 0 0 0 3.5 11H5"/></>,
    eye: <><path d="M1.5 8s2.5-4.5 6.5-4.5S14.5 8 14.5 8 12 12.5 8 12.5 1.5 8 1.5 8z"/><circle cx="8" cy="8" r="1.8"/></>,
    lock: <><rect x="3" y="7" width="10" height="7" rx="1.5"/><path d="M5.5 7V5a2.5 2.5 0 0 1 5 0v2"/></>,
    mail: <><rect x="2" y="3.5" width="12" height="9" rx="1.2"/><path d="m2 5 6 4.5L14 5"/></>,
    star: <><path d="m8 2 1.8 3.7L14 6.3l-3 2.9.7 4.1L8 11.4l-3.7 1.9L5 9.2 2 6.3l4.2-.6z"/></>,
    flag: <><path d="M3 14V2M3 3h8l-2 2.5L11 8H3"/></>,
    link: <><path d="M7 9 9 7M5 9.5 4 10.5a2.5 2.5 0 0 1-3.5-3.5L3 4.5a2.5 2.5 0 0 1 3.5 0M11 6.5 12 5.5a2.5 2.5 0 0 1 3.5 3.5L13 11.5a2.5 2.5 0 0 1-3.5 0"/></>,
    dollar: <><path d="M8 2v12M11 4.5C11 3.4 10 2.5 8 2.5S5 3.4 5 4.5s1 1.7 3 2 3 1 3 2.5-1 2.5-3 2.5-3-1-3-2"/></>,
    target: <><circle cx="8" cy="8" r="6"/><circle cx="8" cy="8" r="3.2"/><circle cx="8" cy="8" r="0.8"/></>,
    zap: <><path d="M9 1 3 9h4l-1 6 6-8H8z"/></>,
    activity: <><path d="M1.5 8h3l2-5 3 10 2-5h3"/></>,
    cube: <><path d="M2 5 8 2l6 3v6L8 14 2 11z"/><path d="m2 5 6 3 6-3M8 8v6"/></>,
    bot: <><rect x="3" y="5" width="10" height="8" rx="2"/><circle cx="6" cy="9" r="0.8"/><circle cx="10" cy="9" r="0.8"/><path d="M8 2v3M5.5 13v1.5M10.5 13v1.5"/></>,
    file: <><path d="M3 1.5h6L13 5.5V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V2.5a1 1 0 0 1 1-1z"/><path d="M9 1.5V5h4"/></>,
    panelLeft: <><rect x="1.5" y="2.5" width="13" height="11" rx="1.5"/><path d="M6 3v10M3.5 6h.5M3.5 8h.5"/></>,
    refresh: <><path d="M2 8a6 6 0 0 1 10-4.5L14 5M14 3v3h-3"/><path d="M14 8a6 6 0 0 1-10 4.5L2 11M2 13v-3h3"/></>,
    receipt: <><path d="M3 1.5v13l1.5-1 1.5 1 1.5-1 1.5 1 1.5-1 1.5 1V1.5z"/><path d="M5.5 5h5M5.5 8h5M5.5 11h3"/></>,
    image: <><rect x="2" y="2.5" width="12" height="11" rx="1.5"/><circle cx="6" cy="6.5" r="1.3"/><path d="m2.5 12 3.5-3.5 3 3 2-2 2.5 2.5"/></>,
    rocket: <><path d="M8 2c3 2 4 5 4 8l-2 2H6l-2-2c0-3 1-6 4-8z"/><circle cx="8" cy="7" r="1.3"/><path d="M4 12l-2 2M14 12l-2 2"/></>,
    menu: <><path d="M2 4h12M2 8h12M2 12h12"/></>,
  };
  return (
    <svg
      width={size} height={size}
      viewBox="0 0 16 16"
      fill="none"
      stroke="currentColor"
      strokeWidth={strokeWidth}
      strokeLinecap="round"
      strokeLinejoin="round"
      aria-hidden="true"
      {...rest}
    >
      {paths[name] || null}
    </svg>
  );
};

/* ============================================================ Avatar */
const Avatar = ({ user = "clarens", size = "md", className = "" }) => {
  const initial = { clarens: "C", kevin: "K", carly: "Y", admin: "A", sys: "S" }[user] || user[0]?.toUpperCase();
  return <span className={`avatar ${className}`} data-user={user} data-size={size}>{initial}</span>;
};

/* ============================================================ Badge */
const Badge = ({ tone = "muted", dot = false, children }) => (
  <span className="badge" data-tone={tone}>
    {dot && <span className="dot" />}
    {children}
  </span>
);

const STATUS = {
  evaluer:   { label: "À évaluer",   tone: "muted",  dot: true },
  recherche: { label: "En recherche",tone: "blue",   dot: true },
  valide:    { label: "Validé",      tone: "violet", dot: true },
  commande:  { label: "Commandé",    tone: "amber",  dot: true },
  stock:     { label: "En stock",    tone: "cyan",   dot: true },
  vendu:     { label: "Vendu",       tone: "green",  dot: true },
};
const StatusBadge = ({ value }) => {
  const s = STATUS[value] || STATUS.evaluer;
  return <Badge tone={s.tone} dot>{s.label}</Badge>;
};

/* ============================================================ Sparkline */
const Sparkline = ({ values, color = "var(--accent)", area = true, height = 28, strokeWidth = 1.5 }) => {
  if (!values?.length) return null;
  const min = Math.min(...values);
  const max = Math.max(...values);
  const range = max - min || 1;
  const W = 100, H = height;
  const pts = values.map((v, i) => [(i / (values.length - 1)) * W, H - ((v - min) / range) * (H - 4) - 2]);
  const path = pts.map(([x, y], i) => (i === 0 ? `M${x} ${y}` : `L${x} ${y}`)).join(" ");
  const areaPath = `${path} L${W} ${H} L0 ${H} Z`;
  const id = useMemo(() => "spark-" + Math.random().toString(36).slice(2, 7), []);
  return (
    <svg className="spark" viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none">
      <defs>
        <linearGradient id={id} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={color} stopOpacity="0.4" />
          <stop offset="100%" stopColor={color} stopOpacity="0" />
        </linearGradient>
      </defs>
      {area && <path d={areaPath} fill={`url(#${id})`} />}
      <path d={path} fill="none" stroke={color} strokeWidth={strokeWidth} vectorEffect="non-scaling-stroke" />
    </svg>
  );
};

/* ============================================================ KPI card */
const KpiCard = ({ label, value, prefix = "", suffix = "", delta, deltaUnit = "%", spark, sparkColor = "var(--accent)", muted = false }) => {
  const positive = delta != null && delta >= 0;
  return (
    <div className="card" style={{ padding: 14, display: "flex", flexDirection: "column", gap: 8, minHeight: 110 }}>
      <div className="between">
        <span style={{ fontSize: 11.5, color: "var(--text-dim)", textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 500 }}>{label}</span>
        {delta != null && (
          <span className="num" style={{
            display: "inline-flex", alignItems: "center", gap: 3,
            fontSize: 11.5, fontWeight: 600,
            color: muted ? "var(--text-muted)" : positive ? "var(--green)" : "var(--red)"
          }}>
            <Icon name={positive ? "arrowUp" : "arrowDown"} size={11} />
            {Math.abs(delta).toFixed(1)}{deltaUnit}
          </span>
        )}
      </div>
      <div className="num" style={{ fontSize: 26, fontWeight: 600, letterSpacing: "-0.025em", lineHeight: 1 }}>
        {prefix}{value}{suffix}
      </div>
      {spark && <Sparkline values={spark} color={sparkColor} height={28} />}
    </div>
  );
};

/* ============================================================ Slider (custom, themed) */
const Slider = ({ value, onChange, min = 0, max = 100, step = 1, format = (v) => v, label, suffix = "", help }) => {
  const pct = ((value - min) / (max - min)) * 100;
  return (
    <div className="stack" style={{ gap: 4 }}>
      <div className="between" style={{ fontSize: 12 }}>
        <span className="muted">{label}</span>
        <span className="num" style={{ fontWeight: 600 }}>{format(value)}{suffix}</span>
      </div>
      <div style={{ position: "relative", height: 18, display: "flex", alignItems: "center" }}>
        <div style={{
          position: "absolute", left: 0, right: 0, top: "50%", height: 4,
          background: "var(--bg-3)", borderRadius: 999, transform: "translateY(-50%)"
        }}/>
        <div style={{
          position: "absolute", left: 0, top: "50%", height: 4, width: `${pct}%`,
          background: "linear-gradient(90deg, var(--accent), var(--accent-strong))",
          borderRadius: 999, transform: "translateY(-50%)"
        }}/>
        <input
          type="range" min={min} max={max} step={step} value={value}
          onChange={(e) => onChange(Number(e.target.value))}
          style={{
            position: "absolute", inset: 0, width: "100%", height: "100%",
            margin: 0, opacity: 0, cursor: "pointer"
          }}
        />
        <div style={{
          position: "absolute", left: `calc(${pct}% - 7px)`, top: "50%",
          width: 14, height: 14, borderRadius: "50%",
          background: "var(--text)", border: "2px solid var(--bg-0)",
          boxShadow: "0 0 0 1px var(--border-strong), 0 2px 4px oklch(0 0 0 / 0.4)",
          transform: "translateY(-50%)", pointerEvents: "none"
        }}/>
      </div>
      {help && <div style={{ fontSize: 10.5, color: "var(--text-faint)" }}>{help}</div>}
    </div>
  );
};

/* ============================================================ Toast system */
const ToastContext = createContext(null);
const ToastProvider = ({ children }) => {
  const [toasts, setToasts] = useState([]);
  const push = useCallback((t) => {
    const id = Math.random().toString(36).slice(2);
    setToasts((s) => [...s, { id, ...t }]);
    setTimeout(() => setToasts((s) => s.filter((x) => x.id !== id)), 4000);
  }, []);
  return (
    <ToastContext.Provider value={push}>
      {children}
      <div className="toasts">
        {toasts.map((t) => (
          <div key={t.id} className="toast">
            <span style={{
              width: 6, height: 6, borderRadius: "50%",
              background: t.tone === "red" ? "var(--red)" : t.tone === "green" ? "var(--green)" : "var(--accent)"
            }}/>
            <span style={{ flex: 1 }}>{t.message}</span>
          </div>
        ))}
      </div>
    </ToastContext.Provider>
  );
};
const useToast = () => useContext(ToastContext);

/* ============================================================ Modal */
const Modal = ({ open, onClose, children, width = 480 }) => {
  useEffect(() => {
    if (!open) return;
    const onKey = (e) => e.key === "Escape" && onClose?.();
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [open, onClose]);
  if (!open) return null;
  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal" style={{ width }} onClick={(e) => e.stopPropagation()}>{children}</div>
    </div>
  );
};

/* ============================================================ Format helpers */
const fmtMoney = (v, ccy = "CAD") => {
  const sign = v < 0 ? "-" : "";
  const abs = Math.abs(v);
  if (abs >= 1_000_000) return sign + (abs / 1_000_000).toFixed(1) + "M";
  if (abs >= 10_000)    return sign + Math.round(abs / 1000) + "k";
  if (abs >= 1000)      return sign + (abs / 1000).toFixed(1) + "k";
  return sign + abs.toFixed(0);
};
const fmtDollar = (v) => "$" + v.toLocaleString("fr-CA", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const fmtPct = (v) => v.toFixed(1) + "%";

/* ============================================================ Inline edit primitives */
/* EditableField — click-to-edit text/number with save on Enter/blur */
const EditableField = ({
  value, onSave, type = "text", placeholder = "—",
  options = null, // [{value, label}] → renders a <select>
  format = (v) => v,
  parse = (v) => v,
  className = "", style = {}, inputStyle = {},
  width = "auto", autoFocus = false, disabled = false,
}) => {
  const [editing, setEditing] = useState(autoFocus);
  const [draft, setDraft] = useState(value ?? "");
  useEffect(() => { setDraft(value ?? ""); }, [value]);
  const commit = () => {
    setEditing(false);
    const parsed = parse(draft);
    if (parsed !== value) onSave?.(parsed);
  };
  const cancel = () => { setDraft(value ?? ""); setEditing(false); };
  if (disabled || !editing) {
    return (
      <span
        className={"editable " + className}
        style={{ cursor: disabled ? "default" : "text", borderRadius: 4, padding: "2px 4px", margin: "-2px -4px", display: "inline-block", minWidth: 20, ...style }}
        onClick={() => !disabled && setEditing(true)}
        title={disabled ? "" : "Cliquer pour modifier"}
      >
        {value === "" || value == null ? <span style={{ color: "var(--text-faint)" }}>{placeholder}</span> : format(value)}
      </span>
    );
  }
  if (options) {
    return (
      <select
        className="input"
        autoFocus
        value={draft}
        onChange={(e) => { setDraft(e.target.value); }}
        onBlur={commit}
        onKeyDown={(e) => { if (e.key === "Enter") commit(); if (e.key === "Escape") cancel(); }}
        style={{ height: 26, fontSize: 12.5, padding: "0 6px", width, ...inputStyle }}
      >
        {options.map((o) => <option key={o.value} value={o.value}>{o.label}</option>)}
      </select>
    );
  }
  return (
    <input
      className="input"
      type={type}
      autoFocus
      value={draft}
      onChange={(e) => setDraft(e.target.value)}
      onBlur={commit}
      onKeyDown={(e) => { if (e.key === "Enter") commit(); if (e.key === "Escape") cancel(); }}
      style={{ height: 26, fontSize: 12.5, padding: "0 6px", width: width === "auto" ? 120 : width, ...inputStyle }}
    />
  );
};

/* EditableArea — multi-line, click-to-edit */
const EditableArea = ({ value, onSave, placeholder = "—", rows = 3, disabled = false }) => {
  const [editing, setEditing] = useState(false);
  const [draft, setDraft] = useState(value ?? "");
  useEffect(() => { setDraft(value ?? ""); }, [value]);
  const commit = () => { setEditing(false); if (draft !== value) onSave?.(draft); };
  if (disabled || !editing) {
    return (
      <div
        className="editable"
        style={{ cursor: disabled ? "default" : "text", padding: 6, borderRadius: 6, border: "1px dashed transparent", minHeight: 24, whiteSpace: "pre-wrap", lineHeight: 1.5 }}
        onClick={() => !disabled && setEditing(true)}
        title={disabled ? "" : "Cliquer pour modifier"}
      >
        {value || <span style={{ color: "var(--text-faint)" }}>{placeholder}</span>}
      </div>
    );
  }
  return (
    <textarea
      className="input" autoFocus rows={rows}
      value={draft}
      onChange={(e) => setDraft(e.target.value)}
      onBlur={commit}
      onKeyDown={(e) => { if (e.key === "Escape") { setDraft(value ?? ""); setEditing(false); } }}
      style={{ width: "100%", padding: 8, fontSize: 12.5, resize: "vertical" }}
    />
  );
};

// Lightweight Editable primitive used by settings.jsx (parity with the zip's ui.jsx).
// Click to edit, commit on Enter/blur, cancel on Escape.
const Editable = ({ value, onChange, type = "text", className = "", style = {}, prefix = "", suffix = "", placeholder = "—", options }) => {
  const [editing, setEditing] = useState(false);
  const [draft, setDraft] = useState(value);
  const ref = useRef(null);
  useEffect(() => { setDraft(value); }, [value]);
  useEffect(() => { if (editing) ref.current?.focus(); }, [editing]);
  const commit = () => {
    setEditing(false);
    if (draft !== value) onChange?.(type === "number" ? Number(draft) : draft);
  };
  const cancel = () => { setDraft(value); setEditing(false); };
  if (editing && options) {
    return (
      <select ref={ref} value={draft}
        onChange={(e) => setDraft(e.target.value)}
        onBlur={commit}
        onKeyDown={(e) => { if (e.key === "Enter") commit(); if (e.key === "Escape") cancel(); }}
        className={"editable-input " + className} style={style}>
        {options.map((o) => <option key={o.value ?? o} value={o.value ?? o}>{o.label ?? o}</option>)}
      </select>
    );
  }
  if (editing) {
    return (
      <input ref={ref} type={type === "number" ? "number" : "text"} value={draft}
        onChange={(e) => setDraft(e.target.value)}
        onBlur={commit}
        onKeyDown={(e) => { if (e.key === "Enter") commit(); if (e.key === "Escape") cancel(); }}
        className={"editable-input " + className} style={style}/>
    );
  }
  return (
    <span className={"editable " + className} style={style}
      onClick={(e) => { e.stopPropagation(); setEditing(true); }}
      title="Cliquer pour modifier">
      {prefix}{value === "" || value == null ? <span style={{ color: "var(--text-faint)" }}>{placeholder}</span> : value}{suffix}
    </span>
  );
};

Object.assign(window, {
  Icon, Avatar, Badge, StatusBadge, STATUS, Sparkline, KpiCard, Slider,
  ToastProvider, useToast, Modal,
  fmtMoney, fmtDollar, fmtPct,
  EditableField, EditableArea, Editable,
});
