// Shared small components

function Pill({ kind = "muted", children }) {
  return (
    <span className={`pill ${kind}`}>
      <span className="dotty"></span>
      {children}
    </span>
  );
}

function FileIcon({ type }) {
  const labels = { xlsx: "XLS", pdf: "PDF", docx: "DOC", csv: "CSV" };
  return <span className={`file-icon ${type}`}>{labels[type] || "FILE"}</span>;
}

function Card({ title, subtitle, action, children, bodyStyle, style, className = "" }) {
  return (
    <div className={`card ${className}`} style={style}>
      {(title || action) && (
        <div className="card-head">
          <div style={{ flex: 1, minWidth: 0 }}>
            {title && <h3 className="card-title">{title}</h3>}
            {subtitle && <p className="card-sub">{subtitle}</p>}
          </div>
          {action}
        </div>
      )}
      <div className="card-body" style={bodyStyle}>{children}</div>
    </div>
  );
}

function Avatar({ name, color, size = 36 }) {
  const bg = color || "linear-gradient(135deg, #b8caea, #6f88b2)";
  const fontSize = size <= 28 ? 11 : size <= 36 ? 13 : 15;
  return (
    <div
      className="avatar"
      style={{
        width: size,
        height: size,
        fontSize,
        background: color ? color : undefined,
      }}
    >
      {initials(name)}
    </div>
  );
}

function CheckBox({ on, onChange }) {
  return (
    <span className={`chk ${on ? "on" : ""}`} onClick={(e) => { e.stopPropagation(); onChange(!on); }}>
      {on && <I.Check size={11} />}
    </span>
  );
}

// Combobox — a text input with type-to-filter suggestions (autocomplete).
// options: [{ value, label, sub? }]. Selecting calls onSelect(value).
// allowFree + onFreeEnter lets a free-typed query be submitted on Enter.
function Combobox({ options = [], value, onSelect, placeholder, emptyText = "Ничего не найдено", style, inputStyle, allowFree = false, onFreeEnter, autoFocus }) {
  const [open, setOpen] = React.useState(false);
  const [q, setQ] = React.useState("");
  const [hi, setHi] = React.useState(0);
  const ref = React.useRef(null);

  const selected = options.find(o => o.value === value);
  const text = open ? q : (selected ? selected.label : (value || ""));
  const ql = q.trim().toLowerCase();
  const filtered = (open && ql)
    ? options.filter(o => o.label.toLowerCase().includes(ql) || (o.sub || "").toLowerCase().includes(ql))
    : options;
  const show = filtered.slice(0, 60);

  React.useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", onDoc);
    return () => document.removeEventListener("mousedown", onDoc);
  }, [open]);

  const choose = (o) => { onSelect && onSelect(o.value); setOpen(false); setQ(""); };
  const onKey = (e) => {
    if (e.key === "ArrowDown") { e.preventDefault(); if (!open) setOpen(true); setHi(h => Math.min(show.length - 1, h + 1)); }
    else if (e.key === "ArrowUp") { e.preventDefault(); setHi(h => Math.max(0, h - 1)); }
    else if (e.key === "Enter") {
      e.preventDefault();
      if (open && show[hi]) choose(show[hi]);
      else if (allowFree && onFreeEnter && q.trim()) { onFreeEnter(q.trim()); setOpen(false); setQ(""); }
    } else if (e.key === "Escape") { setOpen(false); }
  };

  return (
    <div ref={ref} style={{ position: "relative", ...style }}>
      <input
        className="fld" style={{ width: "100%", ...inputStyle }}
        placeholder={placeholder} value={text} autoFocus={autoFocus}
        onChange={e => { setQ(e.target.value); setOpen(true); setHi(0); }}
        onFocus={() => { setQ(""); setOpen(true); setHi(0); }}
        onKeyDown={onKey}
      />
      {open && (
        <div style={{ position: "absolute", top: "calc(100% + 4px)", left: 0, right: 0, background: "var(--paper)", border: "1px solid var(--ink-100)", borderRadius: "var(--r-md)", boxShadow: "var(--sh-2)", maxHeight: 256, overflowY: "auto", zIndex: 300 }}>
          {show.length === 0 ? (
            <div style={{ padding: "10px 12px", fontSize: 12.5, color: "var(--ink-400)" }}>{emptyText}</div>
          ) : show.map((o, i) => (
            <div key={o.value}
              onMouseEnter={() => setHi(i)}
              onMouseDown={(e) => { e.preventDefault(); choose(o); }}
              style={{ display: "flex", justifyContent: "space-between", gap: 10, padding: "8px 11px", fontSize: 13, cursor: "pointer", background: i === hi ? "var(--ink-50)" : "transparent", fontWeight: o.value === value ? 600 : 400 }}>
              <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{o.label}</span>
              {o.sub && <span style={{ color: "var(--ink-400)", fontSize: 11.5, whiteSpace: "nowrap" }}>{o.sub}</span>}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ---- live directory (people/departments) loaded from the backend, not static ----
window.__dirVer = window.__dirVer || 0;
function refreshDirectory() {
  if (!window.API) return Promise.resolve();
  return Promise.all([
    window.API.people ? window.API.people().catch(() => null) : null,
    window.API.departments ? window.API.departments().catch(() => null) : null,
  ]).then(([p, d]) => {
    // Pickers/filters use only active employees; dismissed (active===false) are
    // kept in history but hidden here. The Employees page calls the API directly
    // for the full list.
    if (p && typeof p === "object" && Object.keys(p).length) {
      window.PEOPLE = Object.fromEntries(Object.entries(p).filter(([, v]) => !v || v.active !== false));
    }
    if (Array.isArray(d) && d.length) window.DEPARTMENTS = d;
    window.__dirVer++;
    window.dispatchEvent(new Event("directory-updated"));
  });
}
// useDirectory re-renders the caller whenever the directory is refreshed.
function useDirectory() {
  const [, force] = React.useReducer(x => x + 1, 0);
  React.useEffect(() => {
    const h = () => force();
    window.addEventListener("directory-updated", h);
    return () => window.removeEventListener("directory-updated", h);
  }, []);
  return { people: window.PEOPLE || {}, departments: window.DEPARTMENTS || [] };
}

// MicButton — voice dictation via the browser's built-in Web Speech API
// (Chrome/Edge). Appends each finalized phrase to the target field through
// onText; no backend, no API cost. On unsupported browsers it explains why.
function MicButton({ onText, lang = "ru-RU", size = 15, className = "icon-btn", title = "Голосовой ввод", style }) {
  const [listening, setListening] = React.useState(false);
  const recRef = React.useRef(null);
  const Rec = (typeof window !== "undefined") && (window.SpeechRecognition || window.webkitSpeechRecognition);

  // Stop recognition if the component unmounts mid-recording.
  React.useEffect(() => () => { try { recRef.current && recRef.current.stop(); } catch (_) {} }, []);

  const toggle = () => {
    if (!Rec) {
      alert("Голосовой ввод не поддерживается в этом браузере. Откройте сайт в Chrome или Edge.");
      return;
    }
    if (listening) { try { recRef.current && recRef.current.stop(); } catch (_) {} return; }
    const rec = new Rec();
    rec.lang = lang;
    rec.interimResults = true;
    rec.continuous = true;
    rec.onresult = (e) => {
      for (let i = e.resultIndex; i < e.results.length; i++) {
        if (e.results[i].isFinal) {
          const chunk = (e.results[i][0].transcript || "").trim();
          if (chunk) onText(chunk);
        }
      }
    };
    rec.onerror = () => setListening(false);
    rec.onend = () => setListening(false);
    recRef.current = rec;
    try { rec.start(); setListening(true); } catch (_) { setListening(false); }
  };

  const tip = !Rec ? "Голосовой ввод недоступен (нужен Chrome/Edge)" : (listening ? "Остановить запись" : title);
  return (
    <button
      type="button"
      className={`${className}${listening ? " mic-rec" : ""}`}
      onClick={(e) => { e.stopPropagation(); toggle(); }}
      title={tip}
      aria-label={tip}
      style={{ color: listening ? "var(--err)" : "var(--ink-400)", ...(style || {}) }}
    >
      <I.Mic size={size} />
    </button>
  );
}

Object.assign(window, { Pill, FileIcon, Card, Avatar, CheckBox, Combobox, refreshDirectory, useDirectory, MicButton });
