// 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/.

const { XPCOMUtils } = ChromeUtils.importESModule(
  "resource://gre/modules/XPCOMUtils.sys.mjs"
);
const { FxAccounts } = ChromeUtils.importESModule(
  "resource://gre/modules/FxAccounts.sys.mjs"
);
const { Weave } = ChromeUtils.importESModule(
  "resource://services-sync/main.sys.mjs"
);

ChromeUtils.defineESModuleGetters(this, {
  EventEmitter: "resource://gre/modules/EventEmitter.sys.mjs",
  FxAccountsPairingFlow: "resource://gre/modules/FxAccountsPairing.sys.mjs",
});

const { require } = ChromeUtils.importESModule(
  "resource://devtools/shared/loader/Loader.sys.mjs"
);
const QR = require("devtools/shared/qrcode/index");

// This is only for "labor illusion", see
// https://www.fastcompany.com/3061519/the-ux-secret-that-will-ruin-apps-for-you
const MIN_PAIRING_LOADING_TIME_MS = 1000;

/**
 * Communication between FxAccountsPairingFlow and gFxaPairDeviceDialog
 * is done using an emitter via the following messages:
 * <- [view:SwitchToWebContent] - Notifies the view to navigate to a specific URL.
 * <- [view:Error] - Notifies the view something went wrong during the pairing process.
 * -> [view:Closed] - Notifies the pairing module the view was closed.
 */
var gFxaPairDeviceDialog = {
  init() {
    this._resetBackgroundQR();
    // We let the modal show itself before eventually showing a primary-password dialog later.
    Services.tm.dispatchToMainThread(() => this.startPairingFlow());
  },

  uninit() {
    // When the modal closes we want to remove any query params
    // To prevent refreshes/restores from reopening the dialog
    const browser = window.docShell.chromeEventHandler;
    browser.loadURI(Services.io.newURI("about:preferences#sync"), {
      triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
    });

    this.teardownListeners();
    this._emitter.emit("view:Closed");
  },

  async startPairingFlow() {
    this._resetBackgroundQR();
    document
      .getElementById("qrWrapper")
      .setAttribute("pairing-status", "loading");
    this._emitter = new EventEmitter();
    this.setupListeners();
    try {
      if (!Weave.Utils.ensureMPUnlocked()) {
        throw new Error("Master-password locked.");
      }
      // To keep consistent with our accounts.firefox.com counterpart
      // we restyle the parent dialog this is contained in
      this._styleParentDialog();

      const [, uri] = await Promise.all([
        new Promise(res => setTimeout(res, MIN_PAIRING_LOADING_TIME_MS)),
        FxAccountsPairingFlow.start({ emitter: this._emitter }),
      ]);
      const imgData = QR.encodeToDataURI(uri, "L");
      document.getElementById(
        "qrContainer"
      ).style.backgroundImage = `url("${imgData.src}")`;
      document
        .getElementById("qrWrapper")
        .setAttribute("pairing-status", "ready");
    } catch (e) {
      this.onError(e);
    }
  },

  _styleParentDialog() {
    // Since the dialog title is in the above document, we can't query the
    // document in this level and need to go up one
    let dialogParent = window.parent.document;

    // To allow the firefox icon to go over the dialog
    let dialogBox = dialogParent.querySelector(".dialogBox");
    dialogBox.style.overflow = "visible";
    dialogBox.style.borderRadius = "12px";

    let dialogTitle = dialogParent.querySelector(".dialogTitleBar");
    dialogTitle.style.borderBottom = "none";
    dialogTitle.classList.add("fxaPairDeviceIcon");
  },

  _resetBackgroundQR() {
    // The text we encode doesn't really matter as it is un-scannable (blurry and very transparent).
    const imgData = QR.encodeToDataURI(
      "https://accounts.firefox.com/pair",
      "L"
    );
    document.getElementById(
      "qrContainer"
    ).style.backgroundImage = `url("${imgData.src}")`;
  },

  onError(err) {
    console.error(err);
    this.teardownListeners();
    document
      .getElementById("qrWrapper")
      .setAttribute("pairing-status", "error");
  },

  _switchToUrl(url) {
    const browser = window.docShell.chromeEventHandler;
    browser.fixupAndLoadURIString(url, {
      triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal(
        {}
      ),
    });
  },

  setupListeners() {
    this._switchToWebContent = (_, url) => this._switchToUrl(url);
    this._onError = (_, error) => this.onError(error);
    this._emitter.once("view:SwitchToWebContent", this._switchToWebContent);
    this._emitter.on("view:Error", this._onError);
  },

  teardownListeners() {
    try {
      this._emitter.off("view:SwitchToWebContent", this._switchToWebContent);
      this._emitter.off("view:Error", this._onError);
    } catch (e) {
      console.warn("Error while tearing down listeners.", e);
    }
  },
};
