/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { b64ToPEM, normalizeToKebabCase } from "./utils.mjs";

const l10n = new Localization(["toolkit/about/certviewer.ftl"], true);

export class InfoItem extends HTMLElement {
  constructor(item) {
    super();
    this.item = item;
  }

  connectedCallback() {
    // Attach and connect before adding the template, or fluent
    // won't translate the template copy we insert into the
    // shadowroot.
    this.attachShadow({ mode: "open" });
    document.l10n.connectRoot(this.shadowRoot);

    let infoItemTemplate = document.getElementById("info-item-template");
    this.shadowRoot.appendChild(infoItemTemplate.content.cloneNode(true));

    this.render();
  }

  handleTimeZone(infoElement) {
    let localTime = this.item.info.local;
    let UTCTime = this.item.info.utc;
    infoElement.textContent = UTCTime;
    infoElement.setAttribute("title", localTime);
  }

  addLongHexOverflow(info) {
    info.classList.add("hex");

    // For visual appeal, we want to collapse large hex values into single
    // line items that can be clicked to expand.
    // This function measures the size of the info item relative to its
    // container and adds the "long-hex" class if it's overflowing. Since the
    // container size changes on window resize this function is hooked up to
    // a resize event listener.
    function resize() {
      if (info.classList.contains("hex-open")) {
        info.classList.toggle("long-hex", true);
        return;
      }

      // If the item is not currently drawn and we can't measure its dimensions
      // then attach an observer that will measure it once it appears.
      if (info.clientWidth <= 0) {
        let observer = new IntersectionObserver(function ([
          { intersectionRatio },
        ]) {
          if (intersectionRatio > 0) {
            info.classList.toggle(
              "long-hex",
              info.scrollWidth > info.clientWidth
            );
            observer.unobserve(info);
          }
        }, {});

        observer.observe(info);
      }
      info.classList.toggle("long-hex", info.scrollWidth > info.clientWidth);
    }
    window.addEventListener("resize", resize);
    window.requestAnimationFrame(resize);

    this.addEventListener("mouseup", () => {
      // If a range of text is selected, don't toggle the class that
      // hides/shows additional text.
      if (
        info.classList.contains("long-hex") &&
        window.getSelection().type !== "Range"
      ) {
        info.classList.toggle("hex-open");
      }
    });
  }

  render() {
    let label = this.shadowRoot.querySelector("label");
    let labelId = this.item.labelId;

    // Map specific elements to a different message ID, to allow updates to
    // existing labels and avoid duplicates.
    let stringMapping = {
      signaturealgorithm: "signature-algorithm",
      "rfc-822-name": "email-address",
    };
    let fluentID = stringMapping[labelId] || labelId;

    document.l10n.setAttributes(label, `certificate-viewer-${fluentID}`);

    this.classList.add(labelId);

    let info = this.shadowRoot.querySelector(".info");
    if (this.item.info.hasOwnProperty("utc")) {
      this.handleTimeZone(info);
      return;
    }
    if (labelId === "other-name") {
      document.l10n.setAttributes(info, "certificate-viewer-unsupported");
      return;
    }
    if (typeof this.item.info === "boolean") {
      document.l10n.setAttributes(info, "certificate-viewer-boolean", {
        boolean: this.item.info,
      });
    } else {
      info.textContent = Array.isArray(this.item.info)
        ? this.item.info.join(", ")
        : this.item.info;
    }

    this.classList.add(labelId);

    if (this.item.isHex) {
      this.addLongHexOverflow(info);
    }

    if (labelId === "download") {
      this.setDownloadLinkInformation(info);
    }
  }

  setDownloadLinkInformation(info) {
    let link = document.createElement("a");
    link.setAttribute("href", "data:," + this.item.info);
    link.classList.add("download-link");

    let url = new URL(document.URL);
    let certArray = url.searchParams.getAll("cert");
    let encodedCertArray = [];
    for (let i = 0; i < certArray.length; i++) {
      encodedCertArray.push(encodeURI(b64ToPEM(certArray[i])));
    }
    encodedCertArray = encodedCertArray.join("");

    let chainLink = document.createElement("a");
    chainLink.setAttribute("href", "data:," + encodedCertArray);
    chainLink.classList.add("download-link");
    chainLink.classList.add("download-link-chain");

    info.textContent = "";
    info.appendChild(link);
    info.appendChild(chainLink);

    let certificateLabelElement = document.querySelector(
      "certificate-section"
    ).shadowRoot;

    certificateLabelElement =
      certificateLabelElement
        .querySelector(".subject-name")
        ?.shadowRoot.querySelector(".common-name") ||
      certificateLabelElement
        .querySelector(".subject-alt-names")
        ?.shadowRoot.querySelector(".dns-name");

    certificateLabelElement =
      certificateLabelElement?.shadowRoot.querySelector(".info");

    const certificateLabelText =
      certificateLabelElement?.textContent ||
      l10n.formatValueSync("certificate-viewer-unknown-file-name");

    let fileName = normalizeToKebabCase(certificateLabelText);

    document.l10n.setAttributes(link, "certificate-viewer-download-pem", {
      fileName,
    });

    document.l10n.setAttributes(
      chainLink,
      "certificate-viewer-download-pem-chain",
      {
        fileName,
      }
    );
  }
}

customElements.define("info-item", InfoItem);
