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

/**
 * A JSWindowActor that is responsible for marshalling information between
 * the BackupService singleton and any registered UI widgets that need to
 * represent data from that service. Any UI widgets that want to receive
 * state updates from BackupService should emit a BackupUI:InitWidget
 * event in a document that this actor pair is registered for.
 */
export class BackupUIChild extends JSWindowActorChild {
  #inittedWidgets = new WeakSet();

  /**
   * Handles custom events fired by widgets that want to register with
   * BackupUIChild.
   *
   * @param {Event} event
   *   The custom event that the widget fired.
   */
  async handleEvent(event) {
    /**
     * BackupUI:InitWidget sends a message to the parent to request the BackupService state
     * which will result in a `backupServiceState` property of the widget to be set when that
     * state is received. Subsequent state updates will also cause that state property to
     * be set.
     */
    if (event.type == "BackupUI:InitWidget") {
      this.#inittedWidgets.add(event.target);
      this.sendAsyncMessage("RequestState");
    } else if (event.type == "BackupUI:EnableScheduledBackups") {
      const target = event.target;

      const result = await this.sendQuery(
        "EnableScheduledBackups",
        event.detail
      );
      if (result.success) {
        target.close();
      } else {
        target.enableBackupErrorCode = result.errorCode;
      }
    } else if (event.type == "BackupUI:DisableScheduledBackups") {
      const target = event.target;

      this.sendAsyncMessage("DisableScheduledBackups", event.detail);
      // backups will always end up disabled even if there was an error
      // with other bookkeeping related to turning off backups

      target.close();
    } else if (event.type == "BackupUI:ShowFilepicker") {
      let targetNodeName = event.target.nodeName;
      let { path, filename, iconURL } = await this.sendQuery("ShowFilepicker", {
        win: event.detail?.win,
        filter: event.detail?.filter,
        displayDirectoryPath: event.detail?.displayDirectoryPath,
      });

      let widgets = ChromeUtils.nondeterministicGetWeakSetKeys(
        this.#inittedWidgets
      );

      for (let widget of widgets) {
        if (widget.isConnected && widget.nodeName == targetNodeName) {
          widget.dispatchEvent(
            new CustomEvent("BackupUI:SelectNewFilepickerPath", {
              bubbles: true,
              detail: {
                path,
                filename,
                iconURL,
              },
            })
          );
          break;
        }
      }
    } else if (event.type == "BackupUI:GetBackupFileInfo") {
      let { backupFile } = event.detail;
      this.sendAsyncMessage("GetBackupFileInfo", {
        backupFile,
      });
    } else if (event.type == "BackupUI:RestoreFromBackupFile") {
      let { backupFile, backupPassword } = event.detail;
      event.target.recoveryInProgress = true;
      event.target.recoveryErrorCode = 0;
      let result = await this.sendQuery("RestoreFromBackupFile", {
        backupFile,
        backupPassword,
      });
      event.target.recoveryInProgress = false;
      if (result.success) {
        event.target.restoreFromBackupDialogEl?.close();
      } else {
        event.target.recoveryErrorCode = result.errorCode;
      }
    } else if (event.type == "BackupUI:RestoreFromBackupChooseFile") {
      this.sendAsyncMessage("RestoreFromBackupChooseFile");
    } else if (event.type == "BackupUI:EnableEncryption") {
      const target = event.target;

      const result = await this.sendQuery("EnableEncryption", event.detail);
      if (result.success) {
        target.close();
      } else {
        target.enableEncryptionErrorCode = result.errorCode;
      }
    } else if (event.type == "BackupUI:DisableEncryption") {
      const target = event.target;

      const result = await this.sendQuery("DisableEncryption", event.detail);
      if (result.success) {
        target.close();
      } else {
        target.disableEncryptionErrorCode = result.errorCode;
      }
    } else if (event.type == "BackupUI:RerunEncryption") {
      const target = event.target;

      const result = await this.sendQuery("RerunEncryption", event.detail);
      if (result.success) {
        target.close();
      } else {
        target.rerunEncryptionErrorCode = result.errorCode;
      }
    } else if (event.type == "BackupUI:ShowBackupLocation") {
      this.sendAsyncMessage("ShowBackupLocation");
    } else if (event.type == "BackupUI:EditBackupLocation") {
      this.sendAsyncMessage("EditBackupLocation");
    }
  }

  /**
   * Handles messages sent by BackupUIParent.
   *
   * @param {ReceiveMessageArgument} message
   *   The message received from the BackupUIParent.
   */
  receiveMessage(message) {
    if (message.name == "StateUpdate") {
      let widgets = ChromeUtils.nondeterministicGetWeakSetKeys(
        this.#inittedWidgets
      );
      for (let widget of widgets) {
        if (widget.isConnected) {
          // Note: we might need to switch to using Cu.cloneInto here in the
          // event that these widgets are embedded in a non-parent-process
          // context, like in an onboarding card.
          widget.backupServiceState = message.data.state;
        }
      }
    }
  }
}
