// sbom_editor.jsx — modular, in-line CycloneDX component editor.
//
// Self-contained surface for correcting a parsed SBOM before matching:
// edit name/version/type/supplier in place, add or remove components, and see
// which entries correlate to a tracked CVE live. Kept deliberately decoupled
// (props: value + onChange) so future capabilities — license / hash columns,
// bulk select, VEX status, manual CVE pinning — can slot in without touching
// the wizard. Extension points are flagged with `// + EXTEND:` below.

const SBED_TYPES = [
  "library",
  "application",
  "framework",
  "operating-system",
  "firmware",
  "device",
];

function libVulnCount(lib) {
  return window.VC.libVulnCount(lib);
}

function SbomEditor({ value, onChange, embedded }) {
  const [q, setQ] = useState("");
  const rows = value || [];

  const flagged = useMemo(
    () => rows.filter((r) => libVulnCount(r) > 0).length,
    [rows],
  );
  const shown = useMemo(() => {
    const s = q.trim().toLowerCase();
    return rows
      .map((r, i) => ({ r, i }))
      .filter(
        ({ r }) =>
          !s ||
          r.name.toLowerCase().includes(s) ||
          (r.supplier || "").toLowerCase().includes(s),
      );
  }, [rows, q]);

  function patch(idx, key, val) {
    const next = rows.map((r, i) => (i === idx ? { ...r, [key]: val } : r));
    onChange(next);
  }
  function removeAt(idx) {
    onChange(rows.filter((_, i) => i !== idx));
  }
  function add() {
    const r = window.VC.makeLib("new-component", "0.0.0", "library", "");
    r._id = "l-" + Math.random().toString(36).slice(2, 8);
    onChange([r, ...rows]);
    setQ("");
  }

  return (
    <div className={"sbed" + (embedded ? " sbed-embed" : "")}>
      <div className="sbed-bar">
        <div className="search sbed-search">
          <Icon name="search" size={15} className="search-ic" />
          <input
            value={q}
            onChange={(e) => setQ(e.target.value)}
            placeholder="Filter components…"
          />
          {q && (
            <button className="search-clear" onClick={() => setQ("")}>
              <Icon name="close" size={14} />
            </button>
          )}
        </div>
        <div className="sbed-meta">
          <span className="sbed-count">
            <b>{rows.length}</b> components
          </span>
          {flagged > 0 && (
            <span className="sbed-flag">
              <Icon name="alert" size={13} />
              {flagged} flagged
            </span>
          )}
        </div>
        <button className="btn btn-sm" onClick={add}>
          <Icon name="plus" size={14} />
          Add
        </button>
      </div>

      <div className="sbed-table">
        <div className="sbed-row sbed-head">
          <span></span>
          <span>Component</span>
          <span>Version</span>
          <span>Type</span>
          <span>Supplier</span>
          <span>Status</span>
          <span></span>
        </div>
        {shown.length === 0 && (
          <div className="sbed-empty">
            <Icon name="search" size={18} />
            No components match “{q}”.
          </div>
        )}
        {shown.map(({ r, i }) => {
          const cves = libVulnCount(r);
          return (
            <div
              className={"sbed-row" + (cves ? " is-flagged" : "")}
              key={r._id || i}
            >
              <span className="sbed-grip">
                <Icon name="grip" size={15} />
              </span>
              <div className="sbed-cell sbed-cell-name">
                <input
                  className="cellinput cellinput-strong"
                  value={r.name}
                  onChange={(e) => patch(i, "name", e.target.value)}
                  spellCheck="false"
                />
                <span className="sbed-purl mono">
                  {r.purl ||
                    "pkg:generic/" +
                      (r.name || "").toLowerCase() +
                      "@" +
                      (r.version || "")}
                </span>
              </div>
              <input
                className="cellinput mono"
                value={r.version}
                onChange={(e) => patch(i, "version", e.target.value)}
                spellCheck="false"
              />
              <select
                className="cellinput cellselect"
                value={r.type || "library"}
                onChange={(e) => patch(i, "type", e.target.value)}
              >
                {SBED_TYPES.map((t) => (
                  <option key={t} value={t}>
                    {t}
                  </option>
                ))}
              </select>
              <input
                className="cellinput"
                value={r.supplier || ""}
                placeholder="—"
                onChange={(e) => patch(i, "supplier", e.target.value)}
                spellCheck="false"
              />
              <span className="sbed-status">
                {cves ? (
                  <span
                    className="sbed-cve"
                    title={cves + " tracked CVE(s) reference this library"}
                  >
                    <Icon name="alert" size={12} />
                    {cves} CVE{cves > 1 ? "s" : ""}
                  </span>
                ) : (
                  <span className="sbed-clean">
                    <Icon name="check" size={12} />
                    clear
                  </span>
                )}
              </span>
              {/* + EXTEND: license · integrity hash · VEX state · pin-CVE controls slot here */}
              <button
                className="sbed-del"
                onClick={() => removeAt(i)}
                title="Remove component"
              >
                <Icon name="trash" size={15} />
              </button>
            </div>
          );
        })}
      </div>
    </div>
  );
}

window.SbomEditor = SbomEditor;
