// data_components.jsx — monitored components, CycloneDX SBOM sample, and the
// SBOM→vulnerability correlation engine. Augments window.VC (created in data.jsx).

// Component categories — fallback taxonomy; overridden at runtime by GET /api/meta.
const COMPONENT_CATEGORIES = [
  { id: "ecu",      label: "ECU",       icon: "car",     hint: "Electronic control unit" },
  { id: "gateway",  label: "Gateway",   icon: "browse",  hint: "Domain / central gateway" },
  { id: "firmware", label: "Firmware",  icon: "chip",    hint: "Flashable image / bootloader" },
  { id: "library",  label: "Library",   icon: "layers",  hint: "Software dependency" },
  { id: "sensor",   label: "Sensor",    icon: "target",  hint: "Sensing / perception node" },
];

// ---- CycloneDX correlation ------------------------------------------------
// Normalise a library identifier to its base package name, dropping version
// noise so "wolfssl 5.6.x", "wolfssl@5.6.4" and "wolfSSL" all collapse to "wolfssl".
function normLib(s) {
  return (s || "").toString().toLowerCase().split(/[ @:/]/)[0].replace(/[^a-z0-9.\-_]/g, "").trim();
}

// First fixed version per library-CVE. The DB feed does not carry fixed-version
// data, so this is empty: client-side correlation then matches purely on library
// name. (The Components screen uses the backend's real precomputed matches; this
// map only affects the wizard's pre-save preview.)
const LIB_FIX = {};

function verParts(v) { return (v || "").toString().split(/[^0-9]+/).filter(Boolean).map(Number); }
function compareVer(a, b) {
  const A = verParts(a), B = verParts(b), n = Math.max(A.length, B.length);
  for (let i = 0; i < n; i++) { const x = A[i] || 0, y = B[i] || 0; if (x !== y) return x < y ? -1 : 1; }
  return 0;
}

// Is this CVE still applicable to the given SBOM library (name match + version below fix)?
function libIsVulnerable(lib, v) {
  if (!v.library_name) return false;
  if (normLib(v.library_name) !== normLib(lib.name)) return false;
  const fix = LIB_FIX[v.id];
  if (!fix) return true;
  return compareVer(lib.version, fix) < 0;
}
function libVulnCount(lib) { return window.VC.VULNS.filter(v => libIsVulnerable(lib, v)).length; }

// Highest known fix among the CVEs that reference this library (or null).
function fixedVersionFor(lib) {
  let fix = null;
  window.VC.VULNS.forEach(v => {
    if (normLib(v.library_name) === normLib(lib.name) && LIB_FIX[v.id]) {
      if (!fix || compareVer(LIB_FIX[v.id], fix) > 0) fix = LIB_FIX[v.id];
    }
  });
  return fix;
}
// Simulate a freshly-scanned SBOM where the supplier has patched vulnerable libs
// to their fixed version (used by the "upload new file" update path).
function simulateUpdatedSbom(libs) {
  return (libs || []).map(l => {
    const fix = fixedVersionFor(l);
    return (fix && compareVer(l.version, fix) < 0) ? { ...l, version: fix } : { ...l };
  });
}

// Match an SBOM's components against the tracked CVE ledger (version-aware).
// Returns one entry per distinct still-applicable vulnerability: { vuln, lib }.
function correlateSbom(libs) {
  const VULNS = window.VC.VULNS;
  const seen = {};
  const out = [];
  (libs || []).forEach(lib => {
    if (!normLib(lib.name)) return;
    VULNS.forEach(v => {
      if (libIsVulnerable(lib, v) && !seen[v.id]) {
        seen[v.id] = true;
        out.push({ vuln: v, lib });
      }
    });
  });
  return out.sort((a, b) => window.VC.severityRank(a.vuln.severity) - window.VC.severityRank(b.vuln.severity));
}

// Roll up an SBOM library list into headline stats.
function sbomStats(libs) {
  const types = {};
  (libs || []).forEach(l => { types[l.type || "library"] = (types[l.type || "library"] || 0) + 1; });
  return { total: (libs || []).length, types };
}

// Severity tally for a set of correlation matches.
function matchSeverity(matches) {
  const t = { Critical: 0, High: 0, Medium: 0, Low: 0 };
  matches.forEach(m => { t[m.vuln.severity]++; });
  return t;
}

// Build a normalised library record (used by the SBOM editor when adding rows).
function lib(name, version, type, vendor) {
  return { name, version, type: type || "library",
    purl: `pkg:generic/${(name || "").toLowerCase()}@${version || ""}`, supplier: vendor || "—" };
}

Object.assign(window.VC, {
  COMPONENT_CATEGORIES,
  correlateSbom, normLib, sbomStats, matchSeverity, makeLib: lib,
  LIB_FIX, compareVer, libIsVulnerable, libVulnCount, fixedVersionFor, simulateUpdatedSbom,
});
