/* eslint-disable */
// ============================================================
// PRIMITIVES — building blocks used everywhere
// ============================================================

const { useState: useP_useState, useEffect: useP_useEffect, useRef: useP_useRef } = React;

// ---------- Formatting ----------
const MONTHS = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
function fmtDate(iso, format) {
  if (!iso) return "";
  const d = new Date(iso);
  if (isNaN(d.getTime())) return String(iso);
  const pad = (n, w = 2) => String(n).padStart(w, "0");
  const y = d.getFullYear(), mo = d.getMonth(), da = d.getDate();
  let h = d.getHours(); const mi = d.getMinutes();
  const tt = h >= 12 ? "PM" : "AM"; const h12 = ((h % 12) || 12);
  const f = format || "{0:MMM d, yyyy}";
  return f
    .replace(/\{0:|}/g, "")
    .replace(/yyyy/g, y)
    .replace(/MMMM/g, ["January","February","March","April","May","June","July","August","September","October","November","December"][mo])
    .replace(/MMM/g, MONTHS[mo])
    .replace(/MM/g, pad(mo + 1))
    .replace(/(?<![a-zA-Z])M(?![a-zA-Z])/g, mo + 1)
    .replace(/dd/g, pad(da))
    .replace(/(?<![a-zA-Z])d(?![a-zA-Z])/g, da)
    .replace(/HH/g, pad(h))
    .replace(/(?<![a-zA-Z])H(?![a-zA-Z])/g, h)
    .replace(/hh/g, pad(h12))
    .replace(/(?<![a-zA-Z])h(?![a-zA-Z])/g, h12)
    .replace(/mm/g, pad(mi))
    .replace(/(?<![a-zA-Z])m(?![a-zA-Z])/g, mi)
    .replace(/tt/g, tt);
}
function fmtNumber(n, format) {
  if (n == null || n === "") return "";
  if (!format) return String(n);
  const m = format.match(/\{0:([^}]+)\}/);
  const pat = m ? m[1] : format;
  if (/%/.test(pat)) {
    const decimals = (pat.match(/\.([0#]+)%/) || [,""])[1].length;
    return (Number(n) * 100).toFixed(decimals) + "%";
  }
  if (/^[#0,.]+$/.test(pat)) {
    const decimals = (pat.split(".")[1] || "").length;
    const v = Number(n).toFixed(decimals);
    const [int, dec] = v.split(".");
    const grouped = int.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    return dec ? `${grouped}.${dec}` : grouped;
  }
  return String(n);
}
function fmtCell(value, col) {
  if (value == null) return "";
  switch (col?.DataType) {
    case "datetime":
    case "date":
      return fmtDate(value, col.Format);
    case "int":
    case "decimal":
    case "number":
      return fmtNumber(value, col.Format);
    default:
      return String(value);
  }
}

// Friendly "x ago"
function fmtAgo(iso) {
  if (!iso) return "";
  const d = new Date(iso); if (isNaN(d.getTime())) return "";
  const s = (Date.now() - d.getTime()) / 1000;
  if (s < 60)    return "just now";
  if (s < 3600)  return Math.floor(s / 60) + "m ago";
  if (s < 86400) return Math.floor(s / 3600) + "h ago";
  if (s < 86400 * 30) return Math.floor(s / 86400) + "d ago";
  return fmtDate(iso, "{0:MMM d}");
}

// ---------- Buttons ----------
const Btn = ({ variant = "ghost", size = "sm", className = "", children, ...rest }) => {
  const base = "inline-flex items-center justify-center gap-1.5 font-medium rounded-md transition-all disabled:opacity-40 disabled:cursor-not-allowed select-none whitespace-nowrap shrink-0";
  const sizes = { xs: "h-7 px-2 text-[12px]", sm: "h-8 px-2.5 text-[13px]", md: "h-9 px-3 text-sm", lg: "h-10 px-4 text-sm", icon: "h-8 w-8" };
  const styleByVariant = {
    primary:    { background: "var(--primary)",   color: "white",          border: "1px solid var(--primary)" },
    accent:     { background: "var(--accent)",    color: "white",          border: "1px solid var(--accent)" },
    secondary:  { background: "var(--surface)",   color: "var(--ink)",     border: "1px solid var(--border)" },
    ghost:      { background: "transparent",      color: "var(--muted)",   border: "1px solid transparent" },
    soft:       { background: "var(--surface-3)", color: "var(--ink)",     border: "1px solid transparent" },
    danger:     { background: "var(--danger)",    color: "white",          border: "1px solid var(--danger)" },
    dangerGhost:{ background: "transparent",      color: "var(--danger)",  border: "1px solid transparent" },
  };
  const hoverClass = {
    primary: "hover:brightness-110",
    accent:  "hover:brightness-110",
    secondary: "hover:bg-[var(--surface-2)]",
    ghost:   "hover:bg-[var(--surface-2)] hover:text-[var(--ink)]",
    soft:    "hover:bg-[var(--border)]",
    danger:  "hover:brightness-110",
    dangerGhost: "hover:bg-[var(--danger-bg)]",
  };
  return (
    <button
      className={`${base} ${sizes[size]} ${hoverClass[variant] || ""} ${className}`}
      style={styleByVariant[variant]}
      {...rest}
    >
      {children}
    </button>
  );
};

// ---------- Form primitives ----------
const Field = ({ label, hint, children, className = "" }) => (
  <label className={`block ${className}`}>
    <div className="text-[10.5px] font-semibold uppercase tracking-wider mb-1.5" style={{ color: "var(--muted)" }}>{label}</div>
    {children}
    {hint && <div className="text-[11px] mt-1" style={{ color: "var(--subtle)" }}>{hint}</div>}
  </label>
);

const inputStyle = {
  background: "var(--surface)",
  color: "var(--ink)",
  border: "1px solid var(--border)",
  borderRadius: "7px",
};

const TextInput = ({ value, onChange, className = "", placeholder, disabled, onBlur, onKeyDown, autoFocus }) => (
  <input
    type="text"
    value={value == null ? "" : String(value)}
    onChange={(e) => onChange(e.target.value)}
    onBlur={onBlur}
    onKeyDown={onKeyDown}
    autoFocus={autoFocus}
    placeholder={placeholder}
    disabled={disabled}
    className={`w-full h-8 px-2.5 text-[13px] focus:outline-none focus:border-[var(--primary)] ${className}`}
    style={inputStyle}
  />
);

const NumberInput = ({ value, onChange, min, max, step, disabled, className = "" }) => (
  <input
    type="number"
    value={value == null ? "" : value}
    onChange={(e) => onChange(e.target.value === "" ? "" : Number(e.target.value))}
    min={min} max={max} step={step} disabled={disabled}
    className={`w-full h-8 px-2.5 text-[13px] focus:outline-none focus:border-[var(--primary)] ${className}`}
    style={inputStyle}
  />
);

const Check = ({ checked, onChange, label, disabled }) => (
  <label className={`inline-flex items-center gap-2 select-none ${disabled ? "opacity-50" : "cursor-pointer"}`}>
    <span className="relative inline-flex">
      <input
        type="checkbox"
        checked={!!checked}
        onChange={(e) => onChange(e.target.checked)}
        disabled={disabled}
        className="w-4 h-4 rounded border focus:ring-0"
        style={{ accentColor: "var(--primary)", borderColor: "var(--border-2)" }}
      />
    </span>
    {label && <span className="text-[13px]" style={{ color: "var(--ink-2)" }}>{label}</span>}
  </label>
);

const Toggle = ({ checked, onChange, disabled }) => (
  <button
    type="button"
    onClick={() => !disabled && onChange(!checked)}
    disabled={disabled}
    className={`relative inline-flex h-5 w-9 items-center rounded-full transition-colors ${disabled ? "opacity-50 cursor-not-allowed" : ""}`}
    style={{ background: checked ? "var(--primary)" : "var(--border-2)" }}
  >
    <span
      className="inline-block h-4 w-4 rounded-full shadow transition-transform"
      style={{ background: "white", transform: checked ? "translateX(18px)" : "translateX(2px)" }}
    />
  </button>
);

const Select = ({ value, onChange, options, className = "", disabled }) => (
  <div className={`relative ${className}`}>
    <select
      value={value ?? ""}
      onChange={(e) => onChange(e.target.value)}
      disabled={disabled}
      className="w-full h-8 pl-2.5 pr-7 text-[13px] focus:outline-none focus:border-[var(--primary)] appearance-none"
      style={inputStyle}
    >
      {options.map((o, i) => (
        <option key={(o.value ?? o) + "-" + i} value={o.value ?? o}>{o.label ?? o}</option>
      ))}
    </select>
    <Icon name="chevronDown" size={14} className="absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none" style={{ color: "var(--subtle)" }} />
  </div>
);

// ---------- Avatar ----------
const Avatar = ({ persona, size = 28, ring }) => {
  const dim = { width: size, height: size, fontSize: Math.round(size * 0.42), background: persona.accent };
  return (
    <span
      className="inline-flex items-center justify-center rounded-full font-semibold leading-none shrink-0 select-none text-white"
      style={{ ...dim, boxShadow: ring ? `0 0 0 2px ${ring}` : undefined }}
    >
      {persona.initials}
    </span>
  );
};

// ---------- Role badge ----------
const RoleBadge = ({ role, size = "sm" }) => {
  const c = ENUM_COLORS[role] || { bg: "var(--surface-3)", text: "var(--muted)", dot: "var(--subtle)" };
  const cls = size === "xs" ? "px-1.5 py-0.5 text-[10px]" : "px-2 py-0.5 text-[11px]";
  return (
    <span className={`inline-flex items-center gap-1.5 ${cls} rounded font-medium`}
          style={{ background: c.bg, color: c.text }}>
      <span className="w-1.5 h-1.5 rounded-full" style={{ background: c.dot }}></span>
      {role}
    </span>
  );
};

// ---------- Enum pill (used for cell rendering & filters) ----------
const EnumPill = ({ value, size = "sm" }) => {
  const c = ENUM_COLORS[value] || { bg: "var(--surface-3)", text: "var(--muted)", dot: "var(--subtle)" };
  const cls = size === "xs" ? "px-1.5 py-0.5 text-[10px]" : "px-2 py-0.5 text-[12px]";
  return (
    <span className={`inline-flex items-center gap-1.5 ${cls} rounded font-medium`}
          style={{ background: c.bg, color: c.text }}>
      <span className="w-1.5 h-1.5 rounded-full" style={{ background: c.dot }}></span>
      {value}
    </span>
  );
};

// ---------- Toast ----------
const Toast = ({ t, onClose }) => {
  useP_useEffect(() => {
    const id = setTimeout(onClose, 2800);
    return () => clearTimeout(id);
  }, []);
  const cfg = {
    ok:   { bg: "var(--success)",  icon: "check" },
    warn: { bg: "var(--warning)",  icon: "alertTriangle" },
    err:  { bg: "var(--danger)",   icon: "alert" },
    info: { bg: "var(--ink-2)",    icon: "info" },
  }[t.kind] || { bg: "var(--ink-2)", icon: "check" };
  return (
    <div className="toast-anim text-white rounded-lg shadow-lg px-3 py-2 flex items-center gap-2 max-w-sm" style={{ background: cfg.bg }}>
      <Icon name={cfg.icon} size={14} />
      <span className="text-[13px] flex-1">{t.text}</span>
      <button onClick={onClose} className="text-white/70 hover:text-white"><Icon name="x" size={14} /></button>
    </div>
  );
};

const ToastTray = ({ toasts, remove }) => (
  <div className="fixed bottom-20 right-4 z-50 flex flex-col gap-2 items-end pointer-events-none">
    {toasts.map((t) => (
      <div key={t.id} className="pointer-events-auto">
        <Toast t={t} onClose={() => remove(t.id)} />
      </div>
    ))}
  </div>
);

// ---------- Modal ----------
const Modal = ({ open, title, subtitle, children, onClose, footer, size = "md" }) => {
  useP_useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [open]);
  if (!open) return null;
  const widths = { sm: "max-w-sm", md: "max-w-md", lg: "max-w-2xl", xl: "max-w-4xl" };
  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center p-4 backdrop-fade">
      <div className="absolute inset-0" style={{ background: "rgba(20,22,28,0.55)" }} onClick={onClose}></div>
      <div className={`relative w-full ${widths[size]} card`} style={{ boxShadow: "var(--shadow-lg)" }}>
        <div className="flex items-start gap-3 p-4 border-b" style={{ borderColor: "var(--border)" }}>
          <div className="flex-1 min-w-0">
            <div className="text-[15px] font-semibold" style={{ color: "var(--ink)" }}>{title}</div>
            {subtitle && <div className="text-[13px] mt-0.5" style={{ color: "var(--muted)" }}>{subtitle}</div>}
          </div>
          <button onClick={onClose} className="h-8 w-8 inline-flex items-center justify-center rounded-md hover:bg-[var(--surface-2)]" style={{ color: "var(--muted)" }}>
            <Icon name="x" size={16} />
          </button>
        </div>
        {children && <div className="p-4">{children}</div>}
        {footer && <div className="px-4 py-3 surface-2 border-t flex justify-end gap-2 rounded-b-xl" style={{ borderColor: "var(--border)" }}>{footer}</div>}
      </div>
    </div>
  );
};

// ---------- Page Header ----------
const PageHeader = ({ title, subtitle, badge, right, eyebrow }) => (
  <div className="flex items-start gap-3 mb-5">
    <div className="flex-1 min-w-0">
      {eyebrow && (
        <div className="text-[10.5px] uppercase tracking-[0.14em] font-semibold mb-1.5 inline-flex items-center gap-1.5" style={{ color: "var(--primary)" }}>
          <span className="bullet"></span> {eyebrow}
        </div>
      )}
      <div className="flex items-center gap-2 flex-wrap">
        <h2 className="text-[26px] sm:text-[28px] font-semibold tracking-tight" style={{ color: "var(--ink)", letterSpacing: "-0.018em" }}>{title}</h2>
        {badge}
      </div>
      {subtitle && <p className="text-[13.5px] mt-1.5 max-w-2xl" style={{ color: "var(--muted)" }}>{subtitle}</p>}
    </div>
    {right}
  </div>
);

// ---------- Skeleton ----------
const Skeleton = ({ className = "", style }) => (
  <div className={`shimmer rounded ${className}`} style={style}></div>
);

// ---------- Empty state ----------
const Empty = ({ icon = "search", title, subtitle, action }) => (
  <div className="py-12 px-6 text-center">
    <div className="h-12 w-12 mx-auto mb-3 inline-flex items-center justify-center rounded-full" style={{ background: "var(--surface-3)" }}>
      <Icon name={icon} size={20} style={{ color: "var(--subtle)" }} />
    </div>
    <div className="text-[14px] font-medium" style={{ color: "var(--ink)" }}>{title}</div>
    {subtitle && <div className="text-[12.5px] mt-1" style={{ color: "var(--muted)" }}>{subtitle}</div>}
    {action && <div className="mt-3 inline-flex">{action}</div>}
  </div>
);

// ---------- Popover (positioned dropdown anchored to a ref) ----------
const Popover = ({ open, onClose, anchorRef, children, align = "right", className = "", offset = 4 }) => {
  const ref = useP_useRef(null);
  const [pos, setPos] = useP_useState(null);

  useP_useEffect(() => {
    if (!open) return;

    const place = () => {
      const a = anchorRef?.current;
      if (!a) return;
      const r = a.getBoundingClientRect();
      setPos({
        top: r.bottom + offset,
        left: align === "left" ? r.left : align === "center" ? r.left + r.width / 2 : r.right,
      });
    };
    place();
    window.addEventListener("resize", place);
    window.addEventListener("scroll", place, true);

    const onDown = (e) => {
      if (!ref.current) return;
      if (ref.current.contains(e.target)) return;
      if (anchorRef?.current?.contains(e.target)) return;
      onClose();
    };
    const t = setTimeout(() => document.addEventListener("mousedown", onDown), 0);
    return () => {
      clearTimeout(t);
      document.removeEventListener("mousedown", onDown);
      window.removeEventListener("resize", place);
      window.removeEventListener("scroll", place, true);
    };
  }, [open, anchorRef]);

  if (!open || !pos) return null;
  const transform = align === "center" ? "translateX(-50%)" : align === "right" ? "translateX(-100%)" : undefined;
  return (
    <div
      ref={ref}
      className={`card backdrop-fade ${className}`}
      style={{
        position: "fixed",
        top: pos.top,
        left: pos.left,
        transform,
        zIndex: 60,
        boxShadow: "var(--shadow-md)",
      }}
    >
      {children}
    </div>
  );
};

// ---------- Json highlighter ----------
function highlightJson(jsonString) {
  const escaped = jsonString.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  return escaped.replace(
    /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
    (match) => {
      let cls = "jn";
      if (/^"/.test(match)) cls = /:$/.test(match) ? "jk" : "js";
      else if (/true|false/.test(match)) cls = "jb";
      else if (/null/.test(match)) cls = "ju";
      return `<span class="${cls}">${match}</span>`;
    }
  );
}

// ---------- Compare for sort ----------
const cmp = (a, b, dir, type) => {
  const sign = (dir === "desc" || dir === "0" || dir === 0) ? -1 : 1;
  if (a == null && b == null) return 0;
  if (a == null) return 1;
  if (b == null) return -1;
  if (type === "int" || type === "decimal" || type === "number") return sign * (Number(a) - Number(b));
  if (type === "datetime" || type === "date") return sign * (new Date(a).getTime() - new Date(b).getTime());
  // For enums with a known order set, sort by index
  return sign * String(a).localeCompare(String(b), undefined, { sensitivity: "base" });
};

// ---------- Helpers ----------
function classNames(...xs) { return xs.filter(Boolean).join(" "); }

// Resolve a column array by intersecting the action's Columns with the data row keys.
function resolveColumns(mergedParams, sampleRow) {
  const cols = mergedParams?.Columns || [];
  if (!sampleRow) return [...cols].sort((a, b) => a.ColOrder - b.ColOrder);
  const present = new Set(Object.keys(sampleRow));
  return cols.filter((c) => present.has(c.PropertyName)).sort((a, b) => a.ColOrder - b.ColOrder);
}

// Sort + filter rows according to a Sort[] / Filter[] payload (matches spec)
function applyFilter(rows, filterArr) {
  if (!filterArr || filterArr.length === 0) return rows;
  return rows.filter((row) => {
    return filterArr.every((f) => {
      const value = row[f.PropertyName];
      const op = f.LogicalOperator === "OR" ? "some" : "every";
      return (f.Filters || [])[op]((c) => evalOperator(value, c.Operator, c.Value));
    });
  });
}
function evalOperator(v, op, target) {
  if (op == null) return true;
  const sv = v == null ? "" : String(v).toLowerCase();
  const st = target == null ? "" : String(target).toLowerCase();
  const nv = Number(v), nt = Number(target);
  switch (op) {
    case "Eq":  return String(v) === String(target);
    case "Neq": return String(v) !== String(target);
    case "Contains":           return sv.includes(st);
    case "DoesNotContain":     return !sv.includes(st);
    case "StartsWith":         return sv.startsWith(st);
    case "EndsWith":           return sv.endsWith(st);
    case "IsEmpty":            return v == null || String(v) === "";
    case "IsNotEmpty":         return v != null && String(v) !== "";
    case "IsGreaterThan":      return nv > nt;
    case "IsGreaterThanOrEqualTo": return nv >= nt;
    case "IsLessThan":         return nv < nt;
    case "IsLessThanOrEqualTo":return nv <= nt;
    default: return true;
  }
}
function applySort(rows, sortArr, columns) {
  if (!sortArr || sortArr.length === 0) return rows;
  const ordered = [...sortArr].sort((a, b) => Number(a.ColOrder) - Number(b.ColOrder));
  return [...rows].sort((a, b) => {
    for (const s of ordered) {
      const col = columns.find((c) => c.PropertyName === s.PropertyName);
      const type = col?.DataType || "string";
      // Spec: SortDirection 0=Descending, 1=Ascending. We also accept "asc"/"desc".
      const dir = (s.SortDirection === "1" || s.SortDirection === 1 || s.SortDirection === "asc") ? "asc" : "desc";
      const c = cmp(a[s.PropertyName], b[s.PropertyName], dir, type);
      if (c !== 0) return c;
    }
    return 0;
  });
}

Object.assign(window, {
  fmtDate, fmtNumber, fmtCell, fmtAgo,
  Btn, Field, TextInput, NumberInput, Check, Toggle, Select,
  Avatar, RoleBadge, EnumPill,
  Toast, ToastTray, Modal, PageHeader, Skeleton, Empty, Popover,
  highlightJson, cmp, classNames,
  resolveColumns, applyFilter, applySort, evalOperator,
});
