"use strict";

const documentedConsentEmpty = document.getElementById(
  "documented-consent-empty"
);
const documentedConsentContent = document.getElementById(
  "documented-consent-content"
);
const documentedConsentList = document.getElementById("documented-consent-list");
const consentDetailObservation = document.getElementById(
  "consent-detail-observation"
);
const consentDetailBasics = document.getElementById("consent-detail-basics");
const consentDetailSummary = document.getElementById("consent-detail-summary");
const consentDetailPublisher = document.getElementById(
  "consent-detail-publisher"
);
const consentDetailFieldPaths = document.getElementById(
  "consent-detail-field-paths"
);
const consentDetailRawStrings = document.getElementById(
  "consent-detail-raw-strings"
);
const consentDetailDiagnostics = document.getElementById(
  "consent-detail-diagnostics"
);
const consentDetailJson = document.getElementById("consent-detail-json");

let documentedConsentStates = [];
let selectedConsentStateFingerprint = null;

const FIELD_EXPLANATIONS = {
  firstSeenAt:
    "Zeitpunkt, zu dem dieser Consent-Zustand erstmals lokal beobachtet wurde.",
  lastSeenAt:
    "Zeitpunkt, zu dem derselbe Consent-Zustand zuletzt wieder beobachtet wurde.",
  seenCount: "Wie oft VG-Observe diesen identischen Zustand erkannt hat.",
  "page.origin": "Webseite, auf der dieser Consent-Zustand beobachtet wurde.",
  "page.url": "Vollständige beobachtete Seitenadresse, soweit verfügbar.",
  stateFingerprint: "Technischer Wiedererkennungswert dieses Consent-Zustands.",
  "consent.tcString":
    "Technischer Consent-Nachweis. Für normale Nutzer meist nur als Rohbeleg relevant.",
  "consent.addtlConsent":
    "Zusätzlicher Google-Consent-String außerhalb des normalen TCF-Consent-Strings.",
  "gvl.vendorListVersion":
    "Vendorlisten-Version, die im beobachteten Consent-Kontext gemeldet wurde. Nicht automatisch die aktuellste IAB-Europe-Version.",
  "cmp.tcfPolicyVersion":
    "Version der TCF-Regelgrundlage, die im beobachteten Consent-Kontext gemeldet wurde.",
  "cmp.gdprApplies":
    "Angabe, ob das beobachtete Consent-System DSGVO-Anwendbarkeit gemeldet hat.",
  "observation.eventStatus":
    "Vom beobachteten TCF-Ereignis gemeldeter Status der Consent-Erfassung.",
  "observation.cmpStatus":
    "Vom beobachteten Consent-System gemeldeter Laufzeitstatus.",
  "purposes.consents":
    "Anzahl der Zwecke, für die Zustimmung gemeldet wurde.",
  "purposes.legitimateInterests":
    "Anzahl der Zwecke, für die berechtigtes Interesse gemeldet wurde.",
  "vendors.consents":
    "Anzahl der Firmen, für die Zustimmung gemeldet wurde.",
  "vendors.legitimateInterests":
    "Anzahl der Firmen, die sich laut beobachtetem Kontext auf berechtigtes Interesse stützen.",
  specialFeatureOptins:
    "Anzahl besonderer Funktionen, für die eine aktive Auswahl beobachtet wurde.",
  "vendors.disclosedVendors":
    "Anzahl der im beobachteten tcData-Kontext offengelegten Vendoren/Firmen.",
  "publisher.consents":
    "Anzahl der vom Webseitenbetreiber gemeldeten Publisher-Zustimmungen.",
  "publisher.legitimateInterests":
    "Anzahl der vom Webseitenbetreiber gemeldeten Publisher-Angaben zu berechtigtem Interesse.",
  "publisher.restrictions":
    "Technische Einschränkungen/Vorgaben des Webseitenbetreibers im TCF-Kontext. Für Nutzer nur eingeschränkt aussagekräftig."
};

document.addEventListener("DOMContentLoaded", async () => {
  await renderDocumentedConsentStates();
});

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

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

    documentedConsentStates = result.consentStates ?? [];

    if (documentedConsentStates.length === 0) {
      selectedConsentStateFingerprint = null;
      renderNoDocumentedConsentStates();
      return;
    }

    documentedConsentEmpty.hidden = true;
    documentedConsentContent.hidden = false;

    if (!findDocumentedConsentState(selectedConsentStateFingerprint)) {
      selectedConsentStateFingerprint =
        documentedConsentStates[0]?.stateFingerprint ?? null;
    }

    renderDocumentedConsentStateList();
    renderSelectedConsentState();
  } catch (error) {
    documentedConsentEmpty.hidden = false;
    documentedConsentContent.hidden = true;
    documentedConsentEmpty.textContent =
      "Dokumentierte Consent-Zustände konnten nicht geladen werden.";
    console.warn("VendorGet-IV documented consent states failed", error);
  }
}

function renderNoDocumentedConsentStates() {
  documentedConsentList.textContent = "";
  clearConsentStateDetails();
  documentedConsentEmpty.hidden = false;
  documentedConsentContent.hidden = true;
  documentedConsentEmpty.textContent =
    "Keine dokumentierten Consent-Zustände vorhanden.";
}

function renderDocumentedConsentStateList() {
  documentedConsentList.textContent = "";

  for (const consentState of documentedConsentStates) {
    const row = document.createElement("tr");
    const isSelected =
      consentState?.stateFingerprint === selectedConsentStateFingerprint;

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

    row.addEventListener("click", () => {
      selectDocumentedConsentState(consentState?.stateFingerprint ?? null);
    });

    row.addEventListener("keydown", (event) => {
      if (event.key === "Enter" || event.key === " ") {
        event.preventDefault();
        selectDocumentedConsentState(consentState?.stateFingerprint ?? null);
      }
    });

    appendListCell(row, formatNullable(consentState?.lastSeenAt));
    appendListCell(row, formatNullable(consentState?.firstSeenAt));
    appendListCell(row, formatNullable(consentState?.seenCount), "numeric");
    appendListCell(row, formatNullable(consentState?.page?.origin));
    appendListCell(row, shortenLongString(consentState?.page?.url, 80), "url-cell");
    appendListCell(
      row,
      String(countTruthyObjectValues(consentState?.purposes?.consents)),
      "numeric"
    );
    appendListCell(
      row,
      String(countTruthyObjectValues(consentState?.vendors?.consents)),
      "numeric"
    );
    appendListCell(
      row,
      String(countTruthyObjectValues(consentState?.vendors?.legitimateInterests)),
      "numeric"
    );
    appendListCell(row, formatNullable(consentState?.gvl?.vendorListVersion));
    appendListCell(row, shortenFingerprint(consentState?.stateFingerprint));

    documentedConsentList.append(row);
  }
}

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

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

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

function selectDocumentedConsentState(stateFingerprint) {
  selectedConsentStateFingerprint = stateFingerprint;
  renderDocumentedConsentStateList();
  renderSelectedConsentState();
}

function renderSelectedConsentState() {
  const consentState = findDocumentedConsentState(selectedConsentStateFingerprint);

  clearConsentStateDetails();

  if (!consentState) {
    return;
  }

  renderDetailSection(consentDetailObservation, "Beobachtung", [
    ["Erste Beobachtung", formatNullable(consentState?.firstSeenAt), "firstSeenAt"],
    ["Letzte Beobachtung", formatNullable(consentState?.lastSeenAt), "lastSeenAt"],
    ["Anzahl Beobachtungen", formatNullable(consentState?.seenCount), "seenCount"],
    ["Seite / Origin", formatNullable(consentState?.page?.origin), "page.origin"],
    ["URL", formatLongString(consentState?.page?.url), "page.url"],
    [
      "State-Fingerprint",
      formatLongString(consentState?.stateFingerprint),
      "stateFingerprint"
    ]
  ]);

  renderDetailSection(
    consentDetailBasics,
    "Gemeldete Consent-Grunddaten",
    [
      [
        "Beobachteter TC-String / euconsent-v2",
        summarizeRawString(consentState?.consent?.tcString),
        "consent.tcString"
      ],
      [
        "Beobachteter Google Additional Consent String",
        summarizeRawString(consentState?.consent?.addtlConsent),
        "consent.addtlConsent"
      ],
      [
        "Vom Consent-System gemeldete Vendorlisten-Version",
        formatNullable(consentState?.gvl?.vendorListVersion),
        "gvl.vendorListVersion"
      ],
      [
        "Vom Consent-System gemeldete TCF-Policy-Version",
        formatNullable(consentState?.cmp?.tcfPolicyVersion),
        "cmp.tcfPolicyVersion"
      ],
      [
        "Vom Consent-System gemeldete DSGVO-Anwendbarkeit",
        formatBoolean(consentState?.cmp?.gdprApplies),
        "cmp.gdprApplies"
      ],
      [
        "Beobachteter TCF Event Status",
        formatNullable(consentState?.observation?.eventStatus),
        "observation.eventStatus"
      ],
      [
        "Beobachteter Consent-System-Status",
        formatNullable(consentState?.observation?.cmpStatus),
        "observation.cmpStatus"
      ]
    ]
  );

  renderDetailSection(
    consentDetailSummary,
    "Zusammenfassung der Zwecke und Firmen",
    [
      [
        "Anzahl Purposes mit aktivem Consent",
        String(countTruthyObjectValues(consentState?.purposes?.consents)),
        "purposes.consents"
      ],
      [
        "Anzahl Purposes mit aktivem Legitimate Interest",
        String(countTruthyObjectValues(consentState?.purposes?.legitimateInterests)),
        "purposes.legitimateInterests"
      ],
      [
        "Anzahl Vendoren/Firmen mit aktivem Consent",
        String(countTruthyObjectValues(consentState?.vendors?.consents)),
        "vendors.consents"
      ],
      [
        "Anzahl Vendoren/Firmen mit aktivem Legitimate Interest",
        String(countTruthyObjectValues(consentState?.vendors?.legitimateInterests)),
        "vendors.legitimateInterests"
      ],
      [
        "Anzahl aktivierter Special Features",
        String(countTruthyObjectValues(consentState?.specialFeatureOptins)),
        "specialFeatureOptins"
      ],
      [
        "Anzahl offengelegter Vendoren/Firmen laut beobachtetem tcData-Kontext",
        String(countTruthyObjectValues(consentState?.vendors?.disclosedVendors)),
        "vendors.disclosedVendors"
      ]
    ]
  );

  renderDetailSection(consentDetailPublisher, "Publisher-Angaben", [
    [
      "Publisher Consents: Anzahl aktiv",
      String(countTruthyObjectValues(consentState?.publisher?.consents)),
      "publisher.consents"
    ],
    [
      "Publisher Legitimate Interests: Anzahl aktiv",
      String(countTruthyObjectValues(consentState?.publisher?.legitimateInterests)),
      "publisher.legitimateInterests"
    ],
    [
      "Publisher Restrictions: Anzahl Einträge",
      String(countObjectKeys(consentState?.publisher?.restrictions)),
      "publisher.restrictions"
    ]
  ]);

  consentDetailFieldPaths.textContent = JSON.stringify(
    buildConsentStateFieldPathOverview(),
    null,
    2
  );
  consentDetailRawStrings.textContent = JSON.stringify(
    {
      tcString: consentState?.consent?.tcString ?? null,
      addtlConsent: consentState?.consent?.addtlConsent ?? null
    },
    null,
    2
  );
  consentDetailDiagnostics.textContent = JSON.stringify(
    {
      rawTcData: consentState?.rawTcData ?? null,
      diagnostics: consentState?.diagnostics ?? null
    },
    null,
    2
  );
  consentDetailJson.textContent = JSON.stringify(consentState, null, 2);
}

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

  heading.textContent = title;
  table.className = "inspector-table";

  for (const [label, value, technicalField, helpText] of rows) {
    body.append(createInspectorRow(label, value, technicalField, helpText));
  }

  table.append(body);
  container.append(heading, table);
}

function createInspectorRow(label, value, technicalField, helpText) {
  const row = document.createElement("tr");
  const labelCell = document.createElement("th");
  const valueCell = document.createElement("td");
  const explanationCell = document.createElement("td");
  const mainLabel = document.createElement("span");
  const explanation = helpText ?? FIELD_EXPLANATIONS[technicalField] ?? "-";

  labelCell.scope = "row";
  mainLabel.textContent = label;
  labelCell.append(mainLabel);

  if (technicalField) {
    const technicalFieldElement = document.createElement("small");

    technicalFieldElement.className = "technical-field";
    technicalFieldElement.textContent = `Technisches Feld: ${technicalField}`;
    labelCell.append(technicalFieldElement);
  }

  if (helpText) {
    const helpTextElement = document.createElement("small");

    helpTextElement.className = "inspector-help";
    helpTextElement.textContent = helpText;
    labelCell.append(helpTextElement);
  }

  valueCell.textContent = value;
  valueCell.className = "inspector-value";
  explanationCell.textContent = explanation;
  explanationCell.className = "inspector-explanation";

  row.append(labelCell, valueCell, explanationCell);

  return row;
}

function clearConsentStateDetails() {
  consentDetailObservation.textContent = "";
  consentDetailBasics.textContent = "";
  consentDetailSummary.textContent = "";
  consentDetailPublisher.textContent = "";
  consentDetailFieldPaths.textContent = "";
  consentDetailRawStrings.textContent = "";
  consentDetailDiagnostics.textContent = "";
  consentDetailJson.textContent = "";
}

function findDocumentedConsentState(stateFingerprint) {
  if (!stateFingerprint) {
    return null;
  }

  return (
    documentedConsentStates.find((consentState) => {
      return consentState?.stateFingerprint === stateFingerprint;
    }) ?? null
  );
}

function buildConsentStateFieldPathOverview() {
  return {
    beobachtung: {
      "Erste Beobachtung": "firstSeenAt",
      "Letzte Beobachtung": "lastSeenAt",
      "Anzahl Beobachtungen": "seenCount",
      "Seite / Origin": "page.origin",
      URL: "page.url",
      "State-Fingerprint": "stateFingerprint"
    },
    grunddaten: {
      "TC-String / euconsent-v2": "consent.tcString",
      "Google Additional Consent String": "consent.addtlConsent",
      Vendorliste: "gvl.vendorListVersion",
      "TCF-Policy-Version": "cmp.tcfPolicyVersion",
      "DSGVO-Anwendbarkeit": "cmp.gdprApplies",
      "TCF Event Status": "observation.eventStatus",
      "Consent-System-Status": "observation.cmpStatus"
    },
    zusammenfassung: {
      "Purposes mit Consent": "purposes.consents",
      "Purposes mit Legitimate Interest": "purposes.legitimateInterests",
      "Vendoren/Firmen mit Consent": "vendors.consents",
      "Vendoren/Firmen mit Legitimate Interest": "vendors.legitimateInterests",
      "Special Features": "specialFeatureOptins",
      "Offengelegte Vendoren/Firmen": "vendors.disclosedVendors"
    },
    publisher: {
      "Publisher Consents": "publisher.consents",
      "Publisher Legitimate Interests": "publisher.legitimateInterests",
      "Publisher Restrictions": "publisher.restrictions"
    }
  };
}

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

  return shortenLongString(value, 16);
}

function shortenLongString(value, maxLength) {
  const formattedValue = formatNullable(value);

  if (formattedValue === "-" || formattedValue.length <= maxLength) {
    return formattedValue;
  }

  return `${formattedValue.slice(0, maxLength)}...`;
}

function summarizeRawString(value) {
  if (value === null || value === undefined || value === "") {
    return "nicht vorhanden";
  }

  const stringValue = String(value);
  const prefix = stringValue.slice(0, 32);

  return `vorhanden, Länge ${stringValue.length}, Anfang: ${prefix}${
    stringValue.length > prefix.length ? "..." : ""
  }`;
}

function countTruthyObjectValues(obj) {
  if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
    return 0;
  }

  return Object.values(obj).filter(Boolean).length;
}

function countObjectKeys(obj) {
  if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
    return 0;
  }

  return Object.keys(obj).length;
}

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

  return String(value);
}

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

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

  return "-";
}

function formatLongString(value) {
  return formatNullable(value);
}
