"use strict";

const gvlSnapshotEmpty = document.getElementById("gvl-snapshot-empty");
const gvlSnapshotContent = document.getElementById("gvl-snapshot-content");
const gvlSnapshotList = document.getElementById("gvl-snapshot-list");
const gvlSnapshotSummary = document.getElementById("gvl-snapshot-summary");
const gvlTechnicalFields = document.getElementById("gvl-technical-fields");
const gvlDebugData = document.getElementById("gvl-debug-data");
const gvlFetchOfficialButton = document.getElementById(
  "gvl-fetch-official-button"
);
const gvlRevisionEvidenceExportButton = document.getElementById(
  "gvl-revision-evidence-export-button"
);
const gvlRevisionEvidenceVerifyInput = document.getElementById(
  "gvl-revision-evidence-verify-input"
);
const gvlEvidenceTransportStatus = document.getElementById(
  "gvl-evidence-transport-status"
);
const gvlFetchStatus = document.getElementById("gvl-fetch-status");
const gvlRebuildNormalizedButton = document.getElementById(
  "gvl-rebuild-normalized-button"
);
const gvlRebuildNormalizedStatus = document.getElementById(
  "gvl-rebuild-normalized-status"
);
const gvlVendorOverviewEmpty = document.getElementById(
  "gvl-vendor-overview-empty"
);
const gvlVendorOverviewDetails = document.getElementById(
  "gvl-vendor-overview-details"
);
const gvlVendorOverviewSummary = document.getElementById(
  "gvl-vendor-overview-summary"
);
const gvlVendorOverviewContent = document.getElementById(
  "gvl-vendor-overview-content"
);
const gvlVendorOverviewList = document.getElementById(
  "gvl-vendor-overview-list"
);
const gvlVendorDetailForm = document.getElementById("gvl-vendor-detail-form");
const gvlVendorIdInput = document.getElementById("gvl-vendor-id-input");
const gvlVendorDetailButton = document.getElementById(
  "gvl-vendor-detail-button"
);
const gvlVendorDetailStatus = document.getElementById(
  "gvl-vendor-detail-status"
);
const gvlVendorDetailResult = document.getElementById(
  "gvl-vendor-detail-result"
);

let gvlSnapshots = [];
let selectedSnapshotSha256 = null;
let selectedSnapshotSummary = null;
let selectedSnapshotVendors = [];

document.addEventListener("DOMContentLoaded", async () => {
  gvlFetchOfficialButton.addEventListener("click", async () => {
    await fetchOfficialGvl();
  });

  gvlRevisionEvidenceExportButton.addEventListener("click", async () => {
    await exportSelectedGvlRevisionEvidenceJsonFile();
  });

  gvlRevisionEvidenceVerifyInput.addEventListener("change", async () => {
    await verifyGvlRevisionEvidenceJsonFile();
  });

  gvlRebuildNormalizedButton.addEventListener("click", async () => {
    await rebuildSelectedGvlSnapshotNormalizedData();
  });

  gvlVendorDetailForm.addEventListener("submit", async (event) => {
    event.preventDefault();
    await renderGvlVendorDetail();
  });

  await renderGvlSnapshots();
});

async function fetchOfficialGvl() {
  gvlFetchOfficialButton.disabled = true;
  renderFetchStatus("GVL wird aus Web geladen...");

  try {
    const result = await browser.runtime.sendMessage({
      type: "fetch_official_gvl"
    });

    if (!result?.success) {
      throw new Error(result?.error ?? "official_gvl_fetch_failed");
    }

    renderFetchStatus(buildGvlSyncStatusMessage(result));

    await renderGvlSnapshots();
    await renderSelectedGvlSnapshotSummary();
  } catch (error) {
    renderFetchStatus("GVL aus Web konnte nicht geladen werden.");
    console.warn("VG-Observe manual official GVL fetch failed", error);
  } finally {
    gvlFetchOfficialButton.disabled = false;
  }
}

function buildGvlSyncStatusMessage(result) {
  if (result?.syncStatus === "new_gvl_revision_stored_and_normalized") {
    return "GVL aus Web geladen, neue Revision gespeichert und normalisiert.";
  }

  if (result?.syncStatus === "known_gvl_rebuilt_from_local_evidence") {
    return "GVL aus Web geprüft; bekannte Revision aus lokaler Evidence neu aufgebaut.";
  }

  if (result?.syncStatus === "current_and_locally_available") {
    return "GVL aus Web geprüft; aktuelle Revision ist lokal vollständig verfügbar.";
  }

  return result?.alreadyKnown
    ? "GVL aus Web geprüft; Revision ist lokal verfügbar."
    : "GVL aus Web geladen.";
}

function renderFetchStatus(message) {
  gvlFetchStatus.textContent = message;
}

function renderGvlEvidenceTransportStatus(message, statusKind = "neutral") {
  gvlEvidenceTransportStatus.textContent = message;
  gvlEvidenceTransportStatus.className = [
    "fetch-status",
    "evidence-status",
    `is-${statusKind}`
  ].join(" ");
}

function renderRebuildStatus(message) {
  gvlRebuildNormalizedStatus.textContent = message;
}

async function exportSelectedGvlRevisionEvidenceJsonFile() {
  const snapshot = findGvlSnapshot(selectedSnapshotSha256);

  if (!snapshot) {
    renderGvlEvidenceTransportStatus(
      "Keine GVL-Revision ausgewählt.",
      "warning"
    );
    return;
  }

  gvlRevisionEvidenceExportButton.disabled = true;
  renderGvlEvidenceTransportStatus("GVL-Revision-Export läuft...");

  try {
    const result = await browser.runtime.sendMessage({
      type: "export_gvl_revision_evidence_json",
      payload: {
        snapshotSha256: snapshot.sha256
      }
    });

    if (!result?.success || !result.export) {
      throw new Error(
        result?.error ?? "export_gvl_revision_evidence_json_failed"
      );
    }

    renderGvlEvidenceTransportStatus(
      "GVL-Revision-Export wird intern verifiziert..."
    );

    const verification = await verifyGeneratedGvlRevisionEvidenceExport(
      result.export
    );

    if (!verification.valid) {
      renderGvlEvidenceTransportStatus(
        [
          "GVL-Revision-Export nicht erzeugt: interne Verifikation fehlgeschlagen.",
          buildGvlRevisionEvidenceVerificationMessage(verification)
        ].join(" "),
        "error"
      );
      return;
    }

    downloadGvlRevisionEvidenceJsonExport(result.export);
    renderGvlEvidenceTransportStatus(
      [
        "GVL-Revision exportiert und intern verifiziert.",
        buildGvlRevisionEvidenceExportSuccessMessage(result.export)
      ].join(" "),
      "success"
    );
  } catch (error) {
    renderGvlEvidenceTransportStatus(
      "GVL-Revision-Export fehlgeschlagen.",
      "error"
    );
    console.warn("VG-Observe GVL revision evidence export failed", error);
  } finally {
    gvlRevisionEvidenceExportButton.disabled = false;
  }
}

async function verifyGeneratedGvlRevisionEvidenceExport(exportContainer) {
  const result = await browser.runtime.sendMessage({
    type: "verify_gvl_revision_evidence_json",
    payload: {
      export: exportContainer
    }
  });

  if (!result?.success || !result.verification) {
    throw new Error(
      result?.error ?? "verify_gvl_revision_evidence_json_failed"
    );
  }

  return result.verification;
}

function downloadGvlRevisionEvidenceJsonExport(exportContainer) {
  const json = JSON.stringify(exportContainer, null, 2);
  const blob = new Blob([json], { type: "application/json" });
  const url = URL.createObjectURL(blob);
  const downloadLink = document.createElement("a");
  const metadata = exportContainer?.metadata ?? {};
  const vendorListVersion = metadata.vendorListVersion ?? "unknown";
  const exportedAtUtcCompact =
    metadata.exportedAtUtcCompact ?? formatExportTimestampUtcCompact(new Date());

  downloadLink.href = url;
  downloadLink.download = `GVL-REV-${vendorListVersion}-${exportedAtUtcCompact}.json`;
  document.body.append(downloadLink);
  downloadLink.click();
  downloadLink.remove();

  setTimeout(() => URL.revokeObjectURL(url), 0);
}

function buildGvlRevisionEvidenceExportSuccessMessage(exportContainer) {
  const metadata = exportContainer?.metadata ?? {};
  const recordCount = getGvlRevisionEvidenceNormalizedRecordCount(
    exportContainer?.normalized
  );

  return [
    "GVL-Revision exportiert:",
    `Vendorlisten-Version ${formatNullable(metadata.vendorListVersion)}`,
    `Snapshot ${shortenSha256(metadata.snapshotSha256)}`,
    `Raw-GVL ${shortenSha256(metadata.rawGvlSha256)}`,
    `Payload ${shortenSha256(metadata.exportPayloadSha256)}`,
    `${recordCount} normalisierte Records.`
  ].join(" ");
}

function getGvlRevisionEvidenceNormalizedRecordCount(normalized) {
  return [
    "gvl_vendors",
    "gvl_purposes",
    "gvl_special_purposes",
    "gvl_features",
    "gvl_special_features",
    "gvl_data_categories",
    "gvl_vendor_relationships"
  ].reduce((total, storeName) => {
    return total + (normalized?.[storeName]?.length ?? 0);
  }, 0);
}

async function verifyGvlRevisionEvidenceJsonFile() {
  const file = gvlRevisionEvidenceVerifyInput.files?.[0] ?? null;

  if (!file) {
    return;
  }

  setGvlRevisionEvidenceVerifyDisabled(true);
  renderGvlEvidenceTransportStatus("GVL-Revision-Export wird verifiziert...");

  try {
    const exportContainer = JSON.parse(await file.text());
    const verification = await verifyGeneratedGvlRevisionEvidenceExport(
      exportContainer
    );

    renderGvlEvidenceTransportStatus(
      buildGvlRevisionEvidenceVerificationMessage(verification),
      verification.valid ? "success" : "error"
    );
  } catch (error) {
    renderGvlEvidenceTransportStatus(
      "GVL-Revision-Export ist nicht valide.",
      "error"
    );
    console.warn("VG-Observe GVL revision evidence verification failed", error);
  } finally {
    gvlRevisionEvidenceVerifyInput.value = "";
    setGvlRevisionEvidenceVerifyDisabled(false);
  }
}

function setGvlRevisionEvidenceVerifyDisabled(disabled) {
  const importLabel = document.querySelector(
    "label[for='gvl-revision-evidence-verify-input']"
  );

  gvlRevisionEvidenceVerifyInput.disabled = disabled;
  importLabel?.classList.toggle("is-disabled", disabled);
}

function buildGvlRevisionEvidenceVerificationMessage(verification) {
  const validityLabel = verification.valid ? "valide" : "nicht valide";
  const counts = verification.normalizedCounts ?? {};
  const normalizedRecordCount = Object.values(counts).reduce((total, count) => {
    return total + Number(count ?? 0);
  }, 0);

  return [
    `Verifikation: ${validityLabel}.`,
    `Vendorlisten-Version ${formatNullable(verification.vendorListVersion)}.`,
    `Snapshot ${shortenSha256(verification.snapshotSha256)}.`,
    `Raw-GVL ${shortenSha256(verification.rawGvlSha256)}.`,
    `Payload ${shortenSha256(verification.exportPayloadSha256)}.`,
    `Normalisierte Records: ${normalizedRecordCount} (${formatNormalizedCounts(
      counts
    )}).`,
    verification.valid
      ? ""
      : `Fehler: ${(verification.errors ?? []).join(", ")}.`
  ]
    .filter(Boolean)
    .join(" ");
}

function formatNormalizedCounts(counts) {
  return [
    ["Vendoren", counts.gvl_vendors],
    ["Purposes", counts.gvl_purposes],
    ["Special Purposes", counts.gvl_special_purposes],
    ["Features", counts.gvl_features],
    ["Special Features", counts.gvl_special_features],
    ["Data Categories", counts.gvl_data_categories],
    ["Vendor-Beziehungen", counts.gvl_vendor_relationships]
  ]
    .map(([label, count]) => `${label}: ${Number(count ?? 0)}`)
    .join(", ");
}

async function rebuildSelectedGvlSnapshotNormalizedData() {
  const snapshot = findGvlSnapshot(selectedSnapshotSha256);

  if (!snapshot) {
    renderRebuildStatus("Keine Vendorliste ausgewählt.");
    return;
  }

  gvlRebuildNormalizedButton.disabled = true;
  renderRebuildStatus("Lokale Evidence wird neu normalisiert...");

  try {
    const result = await browser.runtime.sendMessage({
      type: "rebuild_gvl_snapshot_normalized_data",
      payload: {
        sha256: snapshot.sha256
      }
    });

    if (!result?.success) {
      throw new Error(
        result?.error ?? "rebuild_gvl_snapshot_normalized_data_failed"
      );
    }

    await renderSelectedGvlSnapshotSummary();
    renderRebuildStatus(buildRebuildSuccessMessage(result));

    if (gvlVendorIdInput.value) {
      await renderGvlVendorDetail();
    }
  } catch (error) {
    renderRebuildStatus("Lokaler Neuaufbau fehlgeschlagen.");
    console.warn("VG-Observe GVL normalized rebuild failed", error);
  } finally {
    gvlRebuildNormalizedButton.disabled =
      !doesSnapshotNeedNormalizedRebuild(selectedSnapshotSummary);
  }
}

function buildRebuildSuccessMessage(result) {
  const counts = result.counts ?? {};

  return [
    "Lokale GVL-Daten neu aufgebaut.",
    `Vendoren: ${formatCount(counts.vendorCount)}`,
    `Beziehungen: ${formatCount(counts.vendorRelationshipCount)}`
  ].join(" ");
}

async function renderGvlVendorDetail() {
  const vendorId = Number(gvlVendorIdInput.value);

  clearGvlVendorDetail();

  if (!Number.isInteger(vendorId) || vendorId <= 0) {
    renderGvlVendorDetailStatus("Bitte eine gültige Vendor-ID eingeben.");
    return;
  }

  gvlVendorDetailButton.disabled = true;
  renderGvlVendorDetailStatus("Vendor wird geladen...");

  try {
    const result = await browser.runtime.sendMessage({
      type: "get_gvl_vendor_detail",
      payload: {
        vendorId
      }
    });

    if (!result?.success) {
      throw new Error(result?.error ?? "get_gvl_vendor_detail_failed");
    }

    renderGvlVendorDetailStatus("Vendor geladen.");
    renderGvlVendorDetailResult(result.vendorDetail ?? {});
  } catch (error) {
    renderGvlVendorDetailStatus("Vendor konnte nicht geladen werden.");
    console.warn("VG-Observe GVL vendor detail failed", error);
  } finally {
    gvlVendorDetailButton.disabled = false;
  }
}

function clearGvlVendorDetail() {
  gvlVendorDetailResult.textContent = "";
}

function renderGvlVendorDetailStatus(message) {
  gvlVendorDetailStatus.textContent = message;
}

function renderGvlVendorDetailResult(detail) {
  const vendor = detail.vendor ?? {};
  const snapshot = detail.snapshot ?? {};
  const rawEvidence = detail.rawEvidence ?? {};
  const gvlInfo = detail.gvlInfo ?? {};

  gvlVendorDetailResult.textContent = "";
  gvlVendorDetailResult.append(
    buildVendorDetailPanel(vendor, snapshot, rawEvidence, gvlInfo)
  );
}

function buildVendorDetailPanel(vendor, snapshot, rawEvidence, gvlInfo) {
  const details = document.createElement("details");
  const summary = document.createElement("summary");
  const closeButton = document.createElement("button");

  details.className = "vendor-detail-panel";
  details.open = true;
  summary.textContent = `Vendor-Details: ${formatNullable(
    vendor.name
  )} (ID ${formatNullable(vendor.vendorId)})`;

  closeButton.type = "button";
  closeButton.className = "secondary-action";
  closeButton.textContent = "Vendor-Details schließen";
  closeButton.addEventListener("click", () => {
    details.open = false;
  });

  details.append(
    summary,
    buildHumanReadableVendorCard(vendor),
    buildGvlSubjectMatterDetails(gvlInfo),
    buildTechnicalEvidenceDetails(vendor, snapshot, rawEvidence),
    closeButton
  );

  return details;
}

function buildHumanReadableVendorCard(vendor) {
  const rawVendor = vendor.rawVendor ?? {};
  const urlRows = getVendorUrlRows(rawVendor, vendor);

  return buildKeyValueSection("Vendor-Karte", [
    ["Vendor-Name", formatNullable(vendor.name ?? rawVendor.name)],
    ["Vendor-ID", formatNullable(vendor.vendorId ?? rawVendor.id)],
    ["Status", formatVendorStatus(vendor.deletedDate ?? rawVendor.deletedDate)],
    ["Privacy-/Datenschutz-URLs", formatMultilineValue(urlRows.privacy)],
    [
      "Legitimate-Interest-Claim-URLs",
      formatMultilineValue(urlRows.legitimateInterest)
    ],
    [
      "Device-Storage-Disclosure-URL",
      formatNullable(
        vendor.deviceStorageDisclosureUrl ??
          rawVendor.deviceStorageDisclosureUrl
      )
    ],
    ["Nutzt Cookies", formatBooleanGerman(vendor.usesCookies)],
    [
      "Cookie Refresh",
      formatBooleanGerman(rawVendor.cookieRefresh ?? vendor.cookieRefresh)
    ],
    [
      "Cookie Max Age",
      formatCookieMaxAge(vendor.cookieMaxAgeSeconds)
    ],
    [
      "Non-Cookie Access",
      formatBooleanGerman(vendor.usesNonCookieAccess)
    ],
    ["Domains", formatArrayValue(vendor.domains ?? rawVendor.domains)]
  ]);
}

function getVendorUrlRows(rawVendor, vendor) {
  const urls = Array.isArray(rawVendor.urls) ? rawVendor.urls : [];
  const privacy = [];
  const legitimateInterest = [];

  urls.forEach((urlEntry) => {
    if (!urlEntry || typeof urlEntry !== "object") {
      return;
    }

    const label = urlEntry.langId ? `${urlEntry.langId}: ` : "";

    appendUrlIfPresent(privacy, `${label}${urlEntry.privacy}`);
    appendUrlIfPresent(
      legitimateInterest,
      `${label}${urlEntry.legIntClaim}`
    );
    appendUrlIfPresent(
      legitimateInterest,
      `${label}${urlEntry.legitimateInterestClaim}`
    );
  });

  appendUrlIfPresent(privacy, vendor.policyUrl ?? rawVendor.policyUrl);
  appendUrlIfPresent(
    legitimateInterest,
    vendor.legitimateInterestDisclosureUrl ??
      rawVendor.legitimateInterestDisclosureUrl
  );

  return {
    privacy: uniqueValues(privacy),
    legitimateInterest: uniqueValues(legitimateInterest)
  };
}

function appendUrlIfPresent(values, value) {
  if (!value) {
    return;
  }

  const urlValue = String(value);

  if (urlValue.endsWith("undefined") || urlValue.endsWith("null")) {
    return;
  }

  values.push(urlValue);
}

function uniqueValues(values) {
  return Array.from(new Set(values));
}

function buildGvlSubjectMatterDetails(gvlInfo) {
  const details = document.createElement("details");
  const summary = document.createElement("summary");

  details.className = "subject-details";
  details.open = true;
  summary.textContent = "GVL-Fachinformationen";
  details.append(
    summary,
    buildCatalogListSection("Purposes", "Purpose", gvlInfo.purposes),
    buildCatalogListSection(
      "Legitimate-Interest-Purposes",
      "Purpose",
      gvlInfo.legIntPurposes
    ),
    buildCatalogListSection(
      "Flexible Purposes",
      "Purpose",
      gvlInfo.flexiblePurposes
    ),
    buildCatalogListSection(
      "Special Purposes",
      "Special Purpose",
      gvlInfo.specialPurposes
    ),
    buildCatalogListSection("Features", "Feature", gvlInfo.features),
    buildCatalogListSection(
      "Special Features",
      "Special Feature",
      gvlInfo.specialFeatures
    ),
    buildDataDeclarationSection(gvlInfo),
    buildJsonDetails("Data Retention", gvlInfo.dataRetention),
    buildJsonDetails("Overflow", gvlInfo.overflow)
  );

  return details;
}

function buildCatalogListSection(title, entityLabel, items) {
  const section = document.createElement("section");
  const heading = document.createElement("h4");
  const list = document.createElement("div");
  const catalogItems = Array.isArray(items) ? items : [];

  section.className = "gvl-catalog-section";
  heading.textContent = title;
  list.className = "catalog-list";
  section.append(heading);

  if (!catalogItems.length) {
    const empty = document.createElement("p");

    empty.className = "muted-text";
    empty.textContent = "Keine Angaben vorhanden.";
    section.append(empty);
    return section;
  }

  catalogItems.forEach((item) => {
    list.append(buildCatalogItemDetails(entityLabel, item));
  });
  section.append(list);

  return section;
}

function buildCatalogItemDetails(entityLabel, item) {
  const details = document.createElement("details");
  const summary = document.createElement("summary");
  const rows = [
    ["ID", formatNullable(item?.id)],
    ["Name", formatNullable(item?.name)],
    ["Description", formatNullable(item?.description)],
    ["Description Legal", formatNullable(item?.descriptionLegal)],
    ["Illustrations", formatJsonValue(item?.illustrations)]
  ];

  details.className = "catalog-item";
  summary.textContent = formatCatalogItemSummary(entityLabel, item);
  details.append(summary, buildDefinitionList(rows));

  return details;
}

function formatCatalogItemSummary(entityLabel, item) {
  const prefix = `${entityLabel} ${formatNullable(item?.id)}`;

  if (!item?.name) {
    return `${prefix} - Klartext lokal nicht verfügbar`;
  }

  return `${prefix} - ${item.name}`;
}

function buildDataDeclarationSection(gvlInfo) {
  const section = buildCatalogListSection(
    "Data Declaration",
    "Data Category",
    gvlInfo.dataDeclaration
  );

  if (!gvlInfo.dataCategoriesTextAvailable) {
    const note = document.createElement("p");

    note.className = "muted-text";
    note.textContent = "Klartext derzeit nicht normalisiert verfügbar.";
    section.prepend(note);
  }

  return section;
}

function buildTechnicalEvidenceDetails(vendor, snapshot, rawEvidence) {
  const details = document.createElement("details");
  const summary = document.createElement("summary");

  details.className = "technical-details";
  summary.textContent = "Technischer Evidence-Nachweis";
  details.append(
    summary,
    buildKeyValueSection("Snapshot und Raw-GVL", [
      ["Snapshot SHA256", formatNullable(snapshot.snapshotSha256)],
      ["Raw-GVL SHA256", formatNullable(snapshot.rawGvlSha256)],
      ["Vendorlisten-Version", formatNullable(snapshot.vendorListVersion)],
      ["TCF Policy Version", formatNullable(snapshot.tcfPolicyVersion)],
      ["Fetched At", formatNullable(snapshot.fetchedAt)],
      ["Snapshot createdAt", formatNullable(snapshot.createdAt)]
    ]),
    buildKeyValueSection("Raw-GVL-Evidence", [
      ["Source URL", formatNullable(rawEvidence.sourceUrl)],
      ["Fetched At", formatNullable(rawEvidence.fetchedAt)],
      ["HTTP Status", formatNullable(rawEvidence.httpStatus)],
      ["Content-Type", formatNullable(rawEvidence.contentType)],
      ["hasRawBody", formatNullable(rawEvidence.hasRawBody)]
    ]),
    buildJsonDetails("Vollständiger rawVendor-Datensatz", vendor.rawVendor),
    buildJsonDetails("Vollständiger gvl_vendors-Datensatz", vendor)
  );

  return details;
}

function buildDefinitionList(rows) {
  const list = document.createElement("dl");

  list.className = "definition-list";

  rows.forEach(([label, value]) => {
    const term = document.createElement("dt");
    const description = document.createElement("dd");

    term.textContent = label;
    description.textContent = value;
    list.append(term, description);
  });

  return list;
}

function buildKeyValueSection(title, rows) {
  const section = document.createElement("section");
  const heading = document.createElement("h3");
  const table = document.createElement("table");
  const body = document.createElement("tbody");

  section.className = "vendor-detail-section";
  heading.textContent = title;
  table.className = "summary-table";

  for (const [label, value] of rows) {
    const row = document.createElement("tr");
    const labelCell = document.createElement("th");
    const valueCell = document.createElement("td");

    labelCell.scope = "row";
    labelCell.textContent = label;
    valueCell.textContent = value;
    row.append(labelCell, valueCell);
    body.append(row);
  }

  table.append(body);
  section.append(heading, table);

  return section;
}

function buildJsonDetails(title, value) {
  const details = document.createElement("details");
  const summary = document.createElement("summary");
  const valuePre = document.createElement("pre");

  details.className = "technical-details";
  summary.textContent = title;
  valuePre.textContent = JSON.stringify(value ?? null, null, 2);
  details.append(summary, valuePre);

  return details;
}

function formatVendorStatus(deletedDate) {
  if (deletedDate) {
    return `Gelöscht seit ${deletedDate}`;
  }

  return "Aktiv";
}

function formatBooleanGerman(value) {
  if (value === true) {
    return "ja";
  }

  if (value === false) {
    return "nein";
  }

  return "-";
}

function formatCookieMaxAge(value) {
  if (value === null || value === undefined || value === "") {
    return "-";
  }

  const seconds = Number(value);

  if (!Number.isFinite(seconds)) {
    return formatNullable(value);
  }

  const humanReadable = formatDurationFromSeconds(seconds);

  return humanReadable
    ? `${seconds} Sekunden (${humanReadable})`
    : `${seconds} Sekunden`;
}

function formatDurationFromSeconds(seconds) {
  const units = [
    ["Jahr", "Jahre", 365 * 24 * 60 * 60],
    ["Tag", "Tage", 24 * 60 * 60],
    ["Stunde", "Stunden", 60 * 60],
    ["Minute", "Minuten", 60]
  ];

  for (const [singular, plural, unitSeconds] of units) {
    if (seconds >= unitSeconds && seconds % unitSeconds === 0) {
      const amount = seconds / unitSeconds;

      return `${amount} ${amount === 1 ? singular : plural}`;
    }
  }

  return null;
}

function formatArrayValue(value) {
  if (!Array.isArray(value) || !value.length) {
    return "-";
  }

  return value.join("\n");
}

function formatMultilineValue(values) {
  if (!Array.isArray(values) || !values.length) {
    return "-";
  }

  return values.join("\n");
}

async function renderGvlSnapshots() {
  try {
    const result = await browser.runtime.sendMessage({
      type: "list_gvl_snapshots"
    });

    if (!result?.success) {
      throw new Error(result?.error ?? "list_gvl_snapshots_failed");
    }

    gvlSnapshots = result.gvlSnapshots ?? [];

    if (gvlSnapshots.length === 0) {
      renderNoGvlSnapshots();
      return;
    }

    gvlSnapshotEmpty.hidden = true;
    gvlSnapshotContent.hidden = false;

    if (!findGvlSnapshot(selectedSnapshotSha256)) {
      selectedSnapshotSha256 = gvlSnapshots[0]?.sha256 ?? null;
    }

    renderGvlSnapshotList();
    await renderSelectedGvlSnapshotSummary();
  } catch (error) {
    gvlSnapshotEmpty.hidden = false;
    gvlSnapshotContent.hidden = true;
    gvlSnapshotEmpty.textContent =
      "Gespeicherte Vendorlisten konnten nicht geladen werden.";
    console.warn("VG-Observe GVL snapshot list failed", error);
  }
}

function renderNoGvlSnapshots() {
  gvlSnapshotList.textContent = "";
  clearGvlSnapshotSummary();
  clearGvlVendorOverview();
  gvlSnapshotEmpty.hidden = false;
  gvlSnapshotContent.hidden = true;
  gvlSnapshotEmpty.textContent =
    "Keine gespeicherten offiziellen Vendorlisten vorhanden.";
}

function renderGvlSnapshotList() {
  gvlSnapshotList.textContent = "";

  for (const snapshot of gvlSnapshots) {
    const row = document.createElement("tr");
    const isSelected = snapshot?.sha256 === selectedSnapshotSha256;

    row.className = isSelected ? "is-selected" : "";
    row.tabIndex = 0;
    row.setAttribute("role", "button");
    row.setAttribute("aria-pressed", isSelected ? "true" : "false");

    row.addEventListener("click", async () => {
      await selectGvlSnapshot(snapshot?.sha256 ?? null);
    });

    row.addEventListener("keydown", async (event) => {
      if (event.key === "Enter" || event.key === " ") {
        event.preventDefault();
        await selectGvlSnapshot(snapshot?.sha256 ?? null);
      }
    });

    appendListCell(row, formatNullable(snapshot?.vendorListVersion), "numeric");
    appendListCell(row, formatNullable(snapshot?.fetchedAt));
    appendListCell(row, shortenSha256(snapshot?.sha256), "sha-cell");
    appendListCell(row, formatNullable(snapshot?.sourceUrl), "url-cell");

    gvlSnapshotList.append(row);
  }
}

function appendListCell(row, value, className) {
  const cell = document.createElement("td");

  if (className) {
    cell.className = className;
  }

  cell.textContent = value;
  row.append(cell);
}

async function selectGvlSnapshot(sha256) {
  selectedSnapshotSha256 = sha256;
  renderGvlSnapshotList();
  renderRebuildStatus("");
  await renderSelectedGvlSnapshotSummary();
}

async function renderSelectedGvlSnapshotSummary() {
  const snapshot = findGvlSnapshot(selectedSnapshotSha256);

  clearGvlSnapshotSummary();
  clearGvlVendorOverview();

  if (!snapshot) {
    updateRebuildActionState(null);
    return;
  }

  try {
    const result = await browser.runtime.sendMessage({
      type: "get_gvl_snapshot_summary",
      payload: {
        sha256: snapshot.sha256
      }
    });

    if (!result?.success) {
      throw new Error(result?.error ?? "get_gvl_snapshot_summary_failed");
    }

    selectedSnapshotSummary = result.summary ?? {};
    renderSummaryTable(selectedSnapshotSummary);
    updateRebuildActionState(selectedSnapshotSummary);
    await renderVendorOverviewForSelectedSnapshot();
  } catch (error) {
    selectedSnapshotSummary = null;
    updateRebuildActionState(null);
    gvlSnapshotSummary.textContent =
      "Zusammenfassung dieser Vendorliste konnte nicht geladen werden.";
    console.warn("VG-Observe GVL snapshot summary failed", error);
  }
}

async function renderVendorOverviewForSelectedSnapshot() {
  const snapshot = findGvlSnapshot(selectedSnapshotSha256);

  clearGvlVendorOverview();

  if (!snapshot) {
    return;
  }

  try {
    const result = await browser.runtime.sendMessage({
      type: "list_gvl_vendors_for_snapshot",
      payload: {
        sha256: snapshot.sha256
      }
    });

    if (!result?.success) {
      throw new Error(result?.error ?? "list_gvl_vendors_for_snapshot_failed");
    }

    selectedSnapshotVendors = result.vendors ?? [];
    updateVendorOverviewSummary();
    renderVendorOverview();
  } catch (error) {
    gvlVendorOverviewEmpty.hidden = false;
    gvlVendorOverviewEmpty.textContent =
      "Vendoren-Übersicht konnte nicht geladen werden.";
    console.warn("VG-Observe GVL vendor overview failed", error);
  }
}

function renderVendorOverview() {
  gvlVendorOverviewList.textContent = "";

  if (!selectedSnapshotVendors.length) {
    gvlVendorOverviewEmpty.hidden = false;
    gvlVendorOverviewContent.hidden = true;
    gvlVendorOverviewEmpty.textContent =
      "Keine normalisierten Vendoren für diese Vendorliste vorhanden.";
    return;
  }

  gvlVendorOverviewEmpty.hidden = true;
  gvlVendorOverviewContent.hidden = false;

  for (const vendor of selectedSnapshotVendors) {
    const row = document.createElement("tr");

    row.tabIndex = 0;
    row.setAttribute("role", "button");
    row.addEventListener("click", async () => {
      await selectVendorFromOverview(vendor.vendorId);
    });
    row.addEventListener("keydown", async (event) => {
      if (event.key === "Enter" || event.key === " ") {
        event.preventDefault();
        await selectVendorFromOverview(vendor.vendorId);
      }
    });

    appendListCell(row, formatNullable(vendor.vendorId), "numeric");
    appendListCell(row, formatNullable(vendor.name));
    appendListCell(row, formatNullable(vendor.deletedDate));
    gvlVendorOverviewList.append(row);
  }
}

async function selectVendorFromOverview(vendorId) {
  if (vendorId === null || vendorId === undefined) {
    return;
  }

  gvlVendorIdInput.value = String(vendorId);
  await renderGvlVendorDetail();
}

function clearGvlVendorOverview() {
  gvlVendorOverviewList.textContent = "";
  selectedSnapshotVendors = [];
  gvlVendorOverviewEmpty.hidden = true;
  gvlVendorOverviewContent.hidden = true;
  gvlVendorOverviewDetails.open = false;
  updateVendorOverviewSummary();
}

function updateVendorOverviewSummary() {
  const count = selectedSnapshotVendors.length;

  gvlVendorOverviewSummary.textContent = count
    ? `Vendoren-Übersicht anzeigen (${count})`
    : "Vendoren-Übersicht anzeigen";
}

function updateRebuildActionState(summary) {
  const needsRebuild = doesSnapshotNeedNormalizedRebuild(summary);

  gvlRebuildNormalizedButton.disabled = !needsRebuild;

  if (!summary) {
    renderRebuildStatus("");
    return;
  }

  if (needsRebuild) {
    renderRebuildStatus("Reparatur möglich: normalisierte lokale Daten fehlen.");
    return;
  }

  renderRebuildStatus("Normalisierte lokale GVL-Daten sind verfügbar.");
}

function doesSnapshotNeedNormalizedRebuild(summary) {
  if (!summary) {
    return false;
  }

  const expectedVendorCount = Number(summary.snapshotVendorCount ?? 0);
  const normalizedVendorCount = Number(summary.normalizedVendorCount ?? 0);
  const normalizedRelationshipCount = Number(
    summary.normalizedVendorRelationshipCount ?? 0
  );

  if (expectedVendorCount > 0 && normalizedVendorCount < expectedVendorCount) {
    return true;
  }

  return normalizedVendorCount === 0 || normalizedRelationshipCount === 0;
}

function renderSummaryTable(summary) {
  const table = document.createElement("table");
  const body = document.createElement("tbody");
  const rows = [
    ["Vendorlisten-Version", formatNullable(summary.vendorListVersion)],
    ["Abrufzeitpunkt", formatNullable(summary.fetchedAt)],
    ["Quelle", formatNullable(summary.sourceUrl)],
    ["Anzahl Firmen/Vendoren", formatCount(summary.vendorCount)],
    [
      "Normalisierte Vendoren",
      formatCount(summary.normalizedVendorCount)
    ],
    ["Anzahl Zwecke/Purposes", formatCount(summary.purposeCount)],
    ["Anzahl Special Purposes", formatCount(summary.specialPurposeCount)],
    ["Anzahl Features", formatCount(summary.featureCount)],
    ["Anzahl Special Features", formatCount(summary.specialFeatureCount)],
    ["Anzahl Datenkategorien", formatCount(summary.dataCategoryCount)],
    [
      "Anzahl Vendor-Beziehungen",
      formatCount(summary.vendorRelationshipCount)
    ]
  ];

  table.className = "summary-table";

  for (const [label, value] of rows) {
    const row = document.createElement("tr");
    const labelCell = document.createElement("th");
    const valueCell = document.createElement("td");

    labelCell.scope = "row";
    labelCell.textContent = label;
    valueCell.textContent = value;
    row.append(labelCell, valueCell);
    body.append(row);
  }

  table.append(body);
  gvlSnapshotSummary.append(table);

  gvlTechnicalFields.textContent = JSON.stringify(
    summary.technicalFields ?? {},
    null,
    2
  );
  gvlDebugData.textContent = JSON.stringify(
    {
      sha256: summary.sha256 ?? null,
      eventType: summary.eventType ?? null,
      eventCapturedAt: summary.eventCapturedAt ?? null,
      diagnostics: summary.diagnostics ?? null
    },
    null,
    2
  );
}

function clearGvlSnapshotSummary() {
  gvlSnapshotSummary.textContent = "";
  gvlTechnicalFields.textContent = "";
  gvlDebugData.textContent = "";
  selectedSnapshotSummary = null;
}

function findGvlSnapshot(sha256) {
  if (!sha256) {
    return null;
  }

  return (
    gvlSnapshots.find((snapshot) => {
      return snapshot?.sha256 === sha256;
    }) ?? null
  );
}

function formatNullable(value) {
  if (value === null || value === undefined || value === "") {
    return "-";
  }

  return String(value);
}

function formatJsonValue(value) {
  if (value === null || value === undefined || value === "") {
    return "-";
  }

  return JSON.stringify(value);
}

function formatCount(value) {
  if (value === null || value === undefined) {
    return "0";
  }

  return String(value);
}

function shortenSha256(value) {
  if (!value) {
    return "-";
  }

  return `${String(value).slice(0, 12)}...`;
}

function formatExportTimestampUtcCompact(date) {
  const year = date.getUTCFullYear();
  const month = padDatePart(date.getUTCMonth() + 1);
  const day = padDatePart(date.getUTCDate());
  const hours = padDatePart(date.getUTCHours());
  const minutes = padDatePart(date.getUTCMinutes());
  const seconds = padDatePart(date.getUTCSeconds());

  return `${year}${month}${day}T${hours}${minutes}${seconds}Z`;
}

function padDatePart(value) {
  return String(value).padStart(2, "0");
}
