// components.jsx — shared primitives (Card, Pill, Avatar, Metric, Toggle, ScrollList, etc.)

const { useState, useMemo, useRef, useEffect, useLayoutEffect } = React;

function Card({ children, className, style }) {
  return (
    <div
      className={"glass rounded-2xl " + (className || "")}
      style={style}
    >
      {children}
    </div>
  );
}

function SectionTitle({ title, subtitle, action, children }) {
  return (
    <div className="flex items-end justify-between mb-5">
      <div>
        <h2 className="text-[26px] font-semibold tracking-tight">{title}</h2>
        {subtitle && <p className="text-sm text-zinc-500 mt-1">{subtitle}</p>}
      </div>
      <div className="flex items-center gap-2">{action || children}</div>
    </div>
  );
}

function Avatar({ name, color, avatarUrl, size = 36, ring = false }) {
  // Если передан avatarUrl — рендерим <img> с native graceful-fallback:
  // при load-error возвращаемся к инициалам на градиенте. Это даёт
  // прозрачный degradation если бэкенд вернул 404.
  const [imgFailed, setImgFailed] = useState(false);
  const initials = (name || "?").slice(0, 2).toUpperCase();
  const px = size + "px";
  const ringCls = ring ? "ring-2 ring-zinc-900" : "";

  if (avatarUrl && !imgFailed) {
    return (
      <div
        style={{ width: px, height: px }}
        className={"shrink-0 rounded-full overflow-hidden bg-zinc-800 shadow-lg " + ringCls}
      >
        <img
          src={avatarUrl}
          alt={name || ""}
          loading="lazy"
          onError={() => setImgFailed(true)}
          className="w-full h-full object-cover"
        />
      </div>
    );
  }

  return (
    <div
      style={{ width: px, height: px, fontSize: size * 0.34 }}
      className={"shrink-0 rounded-full bg-gradient-to-br " + (color || "from-[#8a7355] to-[#463527]") + " flex items-center justify-center font-semibold text-white shadow-lg " + ringCls}
    >
      {initials}
    </div>
  );
}

const PILL_TONES = {
  emerald: "bg-emerald-500/10 text-emerald-400 border-emerald-500/20",
  rose:    "bg-rose-500/10 text-rose-400 border-rose-500/20",
  amber:   "bg-amber-500/10 text-amber-400 border-amber-500/20",
  indigo:  "bg-indigo-500/10 text-indigo-400 border-indigo-500/20",
  fuchsia: "bg-fuchsia-500/10 text-fuchsia-400 border-fuchsia-500/20",
  sky:     "bg-sky-500/10 text-sky-400 border-sky-500/20",
  zinc:    "bg-zinc-800/60 text-zinc-400 border-zinc-700/60",
};

function Pill({ children, tone = "zinc", className = "" }) {
  return (
    <span className={"inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-[11px] font-medium border " + PILL_TONES[tone] + " " + className}>
      {children}
    </span>
  );
}

function Metric({ label, value, delta, deltaPositive, sublabel, accent }) {
  return (
    <Card className="p-5 hover-lift">
      <div className="text-xs text-zinc-500 font-medium uppercase tracking-wider">{label}</div>
      <div className="flex items-end justify-between mt-2">
        <div className={"text-[34px] leading-none font-semibold tracking-tight tabular-nums " + (accent ? "text-indigo-400" : "")}>{value}</div>
        {delta != null && (
          <div className={"flex items-center gap-1 text-xs font-medium " + (deltaPositive ? "text-emerald-400" : "text-rose-400")}>
            <Icon name={deltaPositive ? "up" : "down"} className="w-3.5 h-3.5" />
            {delta}
          </div>
        )}
      </div>
      {sublabel && <div className="text-[11px] text-zinc-500 mt-1">{sublabel}</div>}
    </Card>
  );
}

function Toggle({ on, onChange, size = "md" }) {
  const sz = size === "sm" ? { w: 28, h: 16, d: 12 } : { w: 36, h: 20, d: 16 };
  return (
    <button
      onClick={() => onChange && onChange(!on)}
      style={{ width: sz.w, height: sz.h }}
      className={"rounded-full relative transition-colors shrink-0 " + (on ? "bg-indigo-500" : "bg-zinc-700")}
    >
      <div
        style={{
          width: sz.d,
          height: sz.d,
          top: (sz.h - sz.d) / 2,
          transform: on ? `translateX(${sz.w - sz.d - (sz.h - sz.d) / 2}px)` : `translateX(${(sz.h - sz.d) / 2}px)`,
          transition: "transform .18s cubic-bezier(.2,.7,.2,1)",
        }}
        className="absolute rounded-full bg-white shadow"
      />
    </button>
  );
}

function IconButton({ icon, tone = "zinc", onClick, label, active }) {
  const tones = {
    zinc: "bg-zinc-800/60 border-zinc-700/60 hover:bg-zinc-800 text-zinc-300",
    indigo: "bg-indigo-500 hover:bg-indigo-600 border-indigo-500 text-white",
    emerald: "bg-emerald-500/10 hover:bg-emerald-500/20 border-emerald-500/30 text-emerald-400",
    rose: "bg-rose-500/10 hover:bg-rose-500/20 border-rose-500/30 text-rose-400",
    ghost: "bg-transparent border-transparent hover:bg-zinc-800/60 text-zinc-400 hover:text-zinc-200",
  };
  return (
    <button
      onClick={onClick}
      title={label}
      className={"inline-flex items-center gap-2 px-3.5 py-2 rounded-lg border text-sm font-medium transition-colors " + tones[tone] + (active ? " ring-2 ring-indigo-500/30" : "")}
    >
      {typeof icon === "string" ? <Icon name={icon} className="w-3.5 h-3.5" /> : icon}
      {label && <span>{label}</span>}
    </button>
  );
}

function SegmentedControl({ value, onChange, options, size = "md" }) {
  const pad = size === "sm" ? "px-2.5 py-1 text-[11px]" : "px-3 py-1.5 text-xs";
  return (
    <div className="inline-flex items-center gap-1 p-1 bg-zinc-800/60 rounded-lg border border-zinc-700/60">
      {options.map((o) => {
        const isActive = value === o.id;
        return (
          <button
            key={o.id}
            onClick={() => onChange(o.id)}
            className={"rounded-md font-medium transition-colors " + pad + " " + (isActive ? "bg-white text-zinc-50 shadow-sm" : "text-zinc-400 hover:text-zinc-200")}
          >
            {o.label}
          </button>
        );
      })}
    </div>
  );
}

function Field({ label, value, defaultValue, placeholder, mono, type = "text", onChange, suffix }) {
  return (
    <div className="grid grid-cols-[120px_1fr] items-center gap-4">
      <div className="text-xs text-zinc-500 font-medium uppercase tracking-wider">{label}</div>
      <div className="relative">
        <input
          type={type}
          value={value}
          defaultValue={defaultValue}
          placeholder={placeholder}
          onChange={onChange}
          className={"w-full bg-zinc-800/60 border border-zinc-700/60 rounded-lg px-3 py-2 text-sm placeholder:text-zinc-500 focus:outline-none focus:border-indigo-500/50 " + (mono ? "font-mono" : "")}
        />
        {suffix && <div className="absolute right-3 top-1/2 -translate-y-1/2 text-xs text-zinc-500">{suffix}</div>}
      </div>
    </div>
  );
}

function SearchInput({ value, onChange, placeholder = "Поиск…", autoFocus, kbd }) {
  return (
    <div className="relative flex-1">
      <Icon name="search" className="w-4 h-4 text-zinc-500 absolute left-3 top-1/2 -translate-y-1/2" />
      <input
        value={value}
        onChange={(e) => onChange(e.target.value)}
        placeholder={placeholder}
        autoFocus={autoFocus}
        className="w-full bg-zinc-900/60 border border-zinc-800 rounded-lg pl-9 pr-12 py-2 text-sm placeholder:text-zinc-500 focus:outline-none focus:border-indigo-500/50"
      />
      {kbd && (
        <kbd className="absolute right-2.5 top-1/2 -translate-y-1/2 text-[10px] font-mono text-zinc-500 bg-zinc-800/80 border border-zinc-700 rounded px-1.5 py-0.5">
          {kbd}
        </kbd>
      )}
    </div>
  );
}

/* ============================
   ScrollList with custom thumb
============================ */

function ScrollList({ children }) {
  const ref = useRef(null);
  const [s, setS] = useState({ top: 0, max: 0, visible: 1 });
  const [drag, setDrag] = useState(false);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const update = () => {
      setS({
        top: el.scrollTop,
        max: el.scrollHeight - el.clientHeight,
        visible: el.clientHeight / el.scrollHeight,
      });
    };
    update();
    el.addEventListener("scroll", update);
    const ro = new ResizeObserver(update);
    ro.observe(el);
    const mo = new MutationObserver(update);
    mo.observe(el, { childList: true, subtree: true });
    return () => {
      el.removeEventListener("scroll", update);
      ro.disconnect();
      mo.disconnect();
    };
  });

  const needsScroll = s.max > 1;

  const onTrackDown = (e) => {
    if (!ref.current) return;
    const track = e.currentTarget;
    const trackRect = track.getBoundingClientRect();
    const thumbH = trackRect.height * s.visible;
    const moveTo = (clientY) => {
      const y = clientY - trackRect.top - thumbH / 2;
      const ratio = Math.max(0, Math.min(1, y / (trackRect.height - thumbH)));
      ref.current.scrollTop = ratio * s.max;
    };
    moveTo(e.clientY);
    setDrag(true);
    const onMove = (ev) => moveTo(ev.clientY);
    const onUp = () => {
      setDrag(false);
      window.removeEventListener("pointermove", onMove);
      window.removeEventListener("pointerup", onUp);
    };
    window.addEventListener("pointermove", onMove);
    window.addEventListener("pointerup", onUp);
  };

  return (
    <div className="relative flex-1 min-h-0">
      <div ref={ref} className="h-full overflow-y-auto px-2 pr-3 space-y-1 hide-scrollbar">
        {children}
      </div>
      {needsScroll && (
        <div onPointerDown={onTrackDown} className="absolute top-0 right-0 w-1.5 h-full cursor-pointer group">
          <div className="absolute inset-y-0 right-0 w-1.5 bg-zinc-800/40 rounded-full" />
          <div
            className={"absolute right-0 w-1.5 rounded-full transition-colors " + (drag ? "bg-indigo-400" : "bg-zinc-600 group-hover:bg-zinc-500")}
            style={{
              height: Math.max(s.visible * 100, 8) + "%",
              top: (s.top / (s.max || 1)) * (100 - Math.max(s.visible * 100, 8)) + "%",
            }}
          />
        </div>
      )}
    </div>
  );
}

/* ============================
   Empty state
============================ */

function EmptyState({ icon, title, hint, action }) {
  return (
    <div className="flex flex-col items-center justify-center text-center py-12 px-4">
      <div className="w-12 h-12 rounded-2xl bg-zinc-800/60 border border-zinc-700/50 flex items-center justify-center mb-3">
        <Icon name={icon} className="w-5 h-5 text-zinc-500" />
      </div>
      <div className="text-zinc-300 font-medium text-sm mb-1">{title}</div>
      {hint && <div className="text-[12px] text-zinc-500 max-w-xs">{hint}</div>}
      {action && <div className="mt-4">{action}</div>}
    </div>
  );
}

/* ============================
   Confidence bar (for AI tags / check verification)
============================ */

function ConfidenceBar({ value, tone = "indigo" }) {
  const pct = Math.round(value * 100);
  const tones = {
    indigo: "bg-indigo-400",
    emerald: "bg-emerald-400",
    amber: "bg-amber-400",
    rose: "bg-rose-400",
  };
  const t = pct >= 90 ? "emerald" : pct >= 70 ? "indigo" : pct >= 50 ? "amber" : "rose";
  return (
    <div className="flex items-center gap-2">
      <div className="flex-1 h-1 bg-zinc-800 rounded-full overflow-hidden">
        <div
          className={"h-full rounded-full " + tones[t]}
          style={{ width: pct + "%", transition: "width .4s ease" }}
        />
      </div>
      <span className="text-[10px] tabular-nums text-zinc-500 font-mono">{pct}%</span>
    </div>
  );
}

/* ============================
   Kbd
============================ */

function Kbd({ children }) {
  return (
    <kbd className="inline-flex items-center justify-center min-w-[20px] h-5 px-1.5 text-[10px] font-mono font-medium text-zinc-400 bg-zinc-800/80 border border-zinc-700/80 rounded">
      {children}
    </kbd>
  );
}

Object.assign(window, {
  Card, SectionTitle, Avatar, Pill, Metric, Toggle, IconButton,
  SegmentedControl, Field, SearchInput, ScrollList, EmptyState,
  ConfidenceBar, Kbd, PILL_TONES,
});
