import {
  ChangeDetectorRef,
  Component,
  NgZone,
  OnDestroy,
  OnInit,
  ViewChild,
  ViewContainerRef,
} from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators";

import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { TotpService } from "@bitwarden/common/abstractions/totp.service";
import { EventType } from "@bitwarden/common/enums";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";

import { SearchBarService } from "../../../app/layout/search/search-bar.service";
import { GeneratorComponent } from "../../../app/tools/generator.component";
import { invokeMenu, RendererMenuItem } from "../../../utils";
import { CollectionsComponent } from "../../../vault/app/vault/collections.component";

import { AddEditComponent } from "./add-edit.component";
import { AttachmentsComponent } from "./attachments.component";
import { FolderAddEditComponent } from "./folder-add-edit.component";
import { PasswordHistoryComponent } from "./password-history.component";
import { ShareComponent } from "./share.component";
import { VaultFilterComponent } from "./vault-filter/vault-filter.component";
import { VaultItemsComponent } from "./vault-items.component";
import { ViewComponent } from "./view.component";

const BroadcasterSubscriptionId = "VaultComponent";

@Component({
  selector: "app-vault",
  templateUrl: "vault.component.html",
})
export class VaultComponent implements OnInit, OnDestroy {
  @ViewChild(ViewComponent) viewComponent: ViewComponent;
  @ViewChild(AddEditComponent) addEditComponent: AddEditComponent;
  @ViewChild(VaultItemsComponent, { static: true }) vaultItemsComponent: VaultItemsComponent;
  @ViewChild("generator", { read: ViewContainerRef, static: true })
  generatorModalRef: ViewContainerRef;
  @ViewChild(VaultFilterComponent, { static: true }) vaultFilterComponent: VaultFilterComponent;
  @ViewChild("attachments", { read: ViewContainerRef, static: true })
  attachmentsModalRef: ViewContainerRef;
  @ViewChild("passwordHistory", { read: ViewContainerRef, static: true })
  passwordHistoryModalRef: ViewContainerRef;
  @ViewChild("share", { read: ViewContainerRef, static: true }) shareModalRef: ViewContainerRef;
  @ViewChild("collections", { read: ViewContainerRef, static: true })
  collectionsModalRef: ViewContainerRef;
  @ViewChild("folderAddEdit", { read: ViewContainerRef, static: true })
  folderAddEditModalRef: ViewContainerRef;

  action: string;
  cipherId: string = null;
  favorites = false;
  type: CipherType = null;
  folderId: string = null;
  collectionId: string = null;
  organizationId: string = null;
  myVaultOnly = false;
  addType: CipherType = null;
  addOrganizationId: string = null;
  addCollectionIds: string[] = null;
  showingModal = false;
  deleted = false;
  userHasPremiumAccess = false;
  activeFilter: VaultFilter = new VaultFilter();

  private modal: ModalRef = null;

  constructor(
    private route: ActivatedRoute,
    private router: Router,
    private i18nService: I18nService,
    private modalService: ModalService,
    private broadcasterService: BroadcasterService,
    private changeDetectorRef: ChangeDetectorRef,
    private ngZone: NgZone,
    private syncService: SyncService,
    private messagingService: MessagingService,
    private platformUtilsService: PlatformUtilsService,
    private eventCollectionService: EventCollectionService,
    private totpService: TotpService,
    private passwordRepromptService: PasswordRepromptService,
    private stateService: StateService,
    private searchBarService: SearchBarService,
    private apiService: ApiService,
    private dialogService: DialogServiceAbstraction
  ) {}

  async ngOnInit() {
    this.userHasPremiumAccess = await this.stateService.getCanAccessPremium();
    this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
      this.ngZone.run(async () => {
        let detectChanges = true;

        switch (message.command) {
          case "newLogin":
            await this.addCipher(CipherType.Login);
            break;
          case "newCard":
            await this.addCipher(CipherType.Card);
            break;
          case "newIdentity":
            await this.addCipher(CipherType.Identity);
            break;
          case "newSecureNote":
            await this.addCipher(CipherType.SecureNote);
            break;
          case "focusSearch":
            (document.querySelector("#search") as HTMLInputElement).select();
            detectChanges = false;
            break;
          case "openGenerator":
            await this.openGenerator(false);
            break;
          case "syncCompleted":
            await this.vaultItemsComponent.reload(this.activeFilter.buildFilter());
            await this.vaultFilterComponent.reloadCollectionsAndFolders(this.activeFilter);
            await this.vaultFilterComponent.reloadOrganizations();
            break;
          case "refreshCiphers":
            this.vaultItemsComponent.refresh();
            break;
          case "modalShown":
            this.showingModal = true;
            break;
          case "modalClosed":
            this.showingModal = false;
            break;
          case "copyUsername": {
            const uComponent =
              this.addEditComponent == null ? this.viewComponent : this.addEditComponent;
            const uCipher = uComponent != null ? uComponent.cipher : null;
            if (
              this.cipherId != null &&
              uCipher != null &&
              uCipher.id === this.cipherId &&
              uCipher.login != null &&
              uCipher.login.username != null
            ) {
              this.copyValue(uCipher, uCipher.login.username, "username", "Username");
            }
            break;
          }
          case "copyPassword": {
            const pComponent =
              this.addEditComponent == null ? this.viewComponent : this.addEditComponent;
            const pCipher = pComponent != null ? pComponent.cipher : null;
            if (
              this.cipherId != null &&
              pCipher != null &&
              pCipher.id === this.cipherId &&
              pCipher.login != null &&
              pCipher.login.password != null &&
              pCipher.viewPassword
            ) {
              this.copyValue(pCipher, pCipher.login.password, "password", "Password");
            }
            break;
          }
          case "copyTotp": {
            const tComponent =
              this.addEditComponent == null ? this.viewComponent : this.addEditComponent;
            const tCipher = tComponent != null ? tComponent.cipher : null;
            if (
              this.cipherId != null &&
              tCipher != null &&
              tCipher.id === this.cipherId &&
              tCipher.login != null &&
              tCipher.login.hasTotp &&
              this.userHasPremiumAccess
            ) {
              const value = await this.totpService.getCode(tCipher.login.totp);
              this.copyValue(tCipher, value, "verificationCodeTotp", "TOTP");
            }
            break;
          }
          default:
            detectChanges = false;
            break;
        }

        if (detectChanges) {
          this.changeDetectorRef.detectChanges();
        }
      });
    });

    if (!this.syncService.syncInProgress) {
      await this.load();
    }
    document.body.classList.remove("layout_frontend");

    this.searchBarService.setEnabled(true);
    this.searchBarService.setPlaceholderText(this.i18nService.t("searchVault"));

    const approveLoginRequests = await this.stateService.getApproveLoginRequests();
    if (approveLoginRequests) {
      const authRequest = await this.apiService.getLastAuthRequest();
      if (authRequest != null) {
        this.messagingService.send("openLoginApproval", {
          notificationId: authRequest.id,
        });
      }
    }
  }

  ngOnDestroy() {
    this.searchBarService.setEnabled(false);
    this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
    document.body.classList.add("layout_frontend");
  }

  async load() {
    // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
    this.route.queryParams.pipe(first()).subscribe(async (params) => {
      if (params.cipherId) {
        const cipherView = new CipherView();
        cipherView.id = params.cipherId;
        if (params.action === "clone") {
          await this.cloneCipher(cipherView);
        } else if (params.action === "edit") {
          await this.editCipher(cipherView);
        } else {
          await this.viewCipher(cipherView);
        }
      } else if (params.action === "add") {
        this.addType = Number(params.addType);
        this.addCipher(this.addType);
      }

      this.activeFilter = new VaultFilter({
        status: params.deleted ? "trash" : params.favorites ? "favorites" : "all",
        cipherType:
          params.action === "add" || params.type == null ? null : parseInt(params.type, null),
        selectedFolderId: params.folderId,
        selectedCollectionId: params.selectedCollectionId,
        selectedOrganizationId: params.selectedOrganizationId,
        myVaultOnly: params.myVaultOnly ?? false,
      });
      await this.vaultItemsComponent.reload(this.activeFilter.buildFilter());
    });
  }

  async viewCipher(cipher: CipherView) {
    if (!(await this.canNavigateAway("view", cipher))) {
      return;
    }

    this.cipherId = cipher.id;
    this.action = "view";
    this.go();
  }

  viewCipherMenu(cipher: CipherView) {
    const menu: RendererMenuItem[] = [
      {
        label: this.i18nService.t("view"),
        click: () =>
          this.functionWithChangeDetection(() => {
            this.viewCipher(cipher);
          }),
      },
    ];
    if (!cipher.isDeleted) {
      menu.push({
        label: this.i18nService.t("edit"),
        click: () =>
          this.functionWithChangeDetection(() => {
            this.editCipher(cipher);
          }),
      });
      if (!cipher.organizationId) {
        menu.push({
          label: this.i18nService.t("clone"),
          click: () =>
            this.functionWithChangeDetection(() => {
              this.cloneCipher(cipher);
            }),
        });
      }
    }

    switch (cipher.type) {
      case CipherType.Login:
        if (
          cipher.login.canLaunch ||
          cipher.login.username != null ||
          cipher.login.password != null
        ) {
          menu.push({ type: "separator" });
        }
        if (cipher.login.canLaunch) {
          menu.push({
            label: this.i18nService.t("launch"),
            click: () => this.platformUtilsService.launchUri(cipher.login.launchUri),
          });
        }
        if (cipher.login.username != null) {
          menu.push({
            label: this.i18nService.t("copyUsername"),
            click: () => this.copyValue(cipher, cipher.login.username, "username", "Username"),
          });
        }
        if (cipher.login.password != null && cipher.viewPassword) {
          menu.push({
            label: this.i18nService.t("copyPassword"),
            click: () => {
              this.copyValue(cipher, cipher.login.password, "password", "Password");
              this.eventCollectionService.collect(EventType.Cipher_ClientCopiedPassword, cipher.id);
            },
          });
        }
        if (cipher.login.hasTotp && (cipher.organizationUseTotp || this.userHasPremiumAccess)) {
          menu.push({
            label: this.i18nService.t("copyVerificationCodeTotp"),
            click: async () => {
              const value = await this.totpService.getCode(cipher.login.totp);
              this.copyValue(cipher, value, "verificationCodeTotp", "TOTP");
            },
          });
        }
        break;
      case CipherType.Card:
        if (cipher.card.number != null || cipher.card.code != null) {
          menu.push({ type: "separator" });
        }
        if (cipher.card.number != null) {
          menu.push({
            label: this.i18nService.t("copyNumber"),
            click: () => this.copyValue(cipher, cipher.card.number, "number", "Card Number"),
          });
        }
        if (cipher.card.code != null) {
          menu.push({
            label: this.i18nService.t("copySecurityCode"),
            click: () => {
              this.copyValue(cipher, cipher.card.code, "securityCode", "Security Code");
              this.eventCollectionService.collect(EventType.Cipher_ClientCopiedCardCode, cipher.id);
            },
          });
        }
        break;
      default:
        break;
    }

    invokeMenu(menu);
  }

  async editCipher(cipher: CipherView) {
    if (!(await this.canNavigateAway("edit", cipher))) {
      return;
    } else if (!(await this.passwordReprompt(cipher))) {
      return;
    }

    await this.editCipherWithoutPasswordPrompt(cipher);
  }

  async editCipherWithoutPasswordPrompt(cipher: CipherView) {
    if (!(await this.canNavigateAway("edit", cipher))) {
      return;
    }

    this.cipherId = cipher.id;
    this.action = "edit";
    this.go();
  }

  async cloneCipher(cipher: CipherView) {
    if (!(await this.canNavigateAway("clone", cipher))) {
      return;
    } else if (!(await this.passwordReprompt(cipher))) {
      return;
    }

    await this.cloneCipherWithoutPasswordPrompt(cipher);
  }

  async cloneCipherWithoutPasswordPrompt(cipher: CipherView) {
    if (!(await this.canNavigateAway("edit", cipher))) {
      return;
    }

    this.cipherId = cipher.id;
    this.action = "clone";
    this.go();
  }

  async addCipher(type: CipherType = null) {
    if (!(await this.canNavigateAway("add", null))) {
      return;
    }

    this.addType = type;
    this.action = "add";
    this.cipherId = null;
    this.prefillNewCipherFromFilter();
    this.go();
  }

  addCipherOptions() {
    const menu: RendererMenuItem[] = [
      {
        label: this.i18nService.t("typeLogin"),
        click: () => this.addCipherWithChangeDetection(CipherType.Login),
      },
      {
        label: this.i18nService.t("typeCard"),
        click: () => this.addCipherWithChangeDetection(CipherType.Card),
      },
      {
        label: this.i18nService.t("typeIdentity"),
        click: () => this.addCipherWithChangeDetection(CipherType.Identity),
      },
      {
        label: this.i18nService.t("typeSecureNote"),
        click: () => this.addCipherWithChangeDetection(CipherType.SecureNote),
      },
    ];

    invokeMenu(menu);
  }

  async savedCipher(cipher: CipherView) {
    this.cipherId = cipher.id;
    this.action = "view";
    this.go();
    await this.vaultItemsComponent.refresh();
  }

  async deletedCipher(cipher: CipherView) {
    this.cipherId = null;
    this.action = null;
    this.go();
    await this.vaultItemsComponent.refresh();
  }

  async restoredCipher(cipher: CipherView) {
    this.cipherId = null;
    this.action = null;
    this.go();
    await this.vaultItemsComponent.refresh();
  }

  async editCipherAttachments(cipher: CipherView) {
    if (this.modal != null) {
      this.modal.close();
    }

    const [modal, childComponent] = await this.modalService.openViewRef(
      AttachmentsComponent,
      this.attachmentsModalRef,
      (comp) => (comp.cipherId = cipher.id)
    );
    this.modal = modal;

    let madeAttachmentChanges = false;
    // eslint-disable-next-line rxjs-angular/prefer-takeuntil
    childComponent.onUploadedAttachment.subscribe(() => (madeAttachmentChanges = true));
    // eslint-disable-next-line rxjs-angular/prefer-takeuntil
    childComponent.onDeletedAttachment.subscribe(() => (madeAttachmentChanges = true));

    // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
    this.modal.onClosed.subscribe(async () => {
      this.modal = null;
      if (madeAttachmentChanges) {
        await this.vaultItemsComponent.refresh();
      }
      madeAttachmentChanges = false;
    });
  }

  async shareCipher(cipher: CipherView) {
    if (this.modal != null) {
      this.modal.close();
    }

    const [modal, childComponent] = await this.modalService.openViewRef(
      ShareComponent,
      this.shareModalRef,
      (comp) => (comp.cipherId = cipher.id)
    );
    this.modal = modal;

    // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
    childComponent.onSharedCipher.subscribe(async () => {
      this.modal.close();
      this.viewCipher(cipher);
      await this.vaultItemsComponent.refresh();
    });
    // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
    this.modal.onClosed.subscribe(async () => {
      this.modal = null;
    });
  }

  async cipherCollections(cipher: CipherView) {
    if (this.modal != null) {
      this.modal.close();
    }

    const [modal, childComponent] = await this.modalService.openViewRef(
      CollectionsComponent,
      this.collectionsModalRef,
      (comp) => (comp.cipherId = cipher.id)
    );
    this.modal = modal;

    // eslint-disable-next-line rxjs-angular/prefer-takeuntil
    childComponent.onSavedCollections.subscribe(() => {
      this.modal.close();
      this.viewCipher(cipher);
    });
    // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
    this.modal.onClosed.subscribe(async () => {
      this.modal = null;
    });
  }

  async viewCipherPasswordHistory(cipher: CipherView) {
    if (this.modal != null) {
      this.modal.close();
    }

    [this.modal] = await this.modalService.openViewRef(
      PasswordHistoryComponent,
      this.passwordHistoryModalRef,
      (comp) => (comp.cipherId = cipher.id)
    );

    // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
    this.modal.onClosed.subscribe(async () => {
      this.modal = null;
    });
  }

  cancelledAddEdit(cipher: CipherView) {
    this.cipherId = cipher.id;
    this.action = this.cipherId != null ? "view" : null;
    this.go();
  }

  async applyVaultFilter(vaultFilter: VaultFilter) {
    this.searchBarService.setPlaceholderText(
      this.i18nService.t(this.calculateSearchBarLocalizationString(vaultFilter))
    );
    this.activeFilter = vaultFilter;
    await this.vaultItemsComponent.reload(
      this.activeFilter.buildFilter(),
      vaultFilter.status === "trash"
    );
    this.go();
  }

  private calculateSearchBarLocalizationString(vaultFilter: VaultFilter): string {
    if (vaultFilter.status === "favorites") {
      return "searchFavorites";
    }
    if (vaultFilter.status === "trash") {
      return "searchTrash";
    }
    if (vaultFilter.cipherType != null) {
      return "searchType";
    }
    if (vaultFilter.selectedFolderId != null && vaultFilter.selectedFolderId != "none") {
      return "searchFolder";
    }
    if (vaultFilter.selectedCollectionId != null) {
      return "searchCollection";
    }
    if (vaultFilter.selectedOrganizationId != null) {
      return "searchOrganization";
    }
    if (vaultFilter.myVaultOnly) {
      return "searchMyVault";
    }

    return "searchVault";
  }

  async openGenerator(comingFromAddEdit: boolean, passwordType = true) {
    if (this.modal != null) {
      this.modal.close();
    }

    const cipher = this.addEditComponent?.cipher;
    const loginType = cipher != null && cipher.type === CipherType.Login && cipher.login != null;

    const [modal, childComponent] = await this.modalService.openViewRef(
      GeneratorComponent,
      this.generatorModalRef,
      (comp) => {
        comp.comingFromAddEdit = comingFromAddEdit;
        if (comingFromAddEdit) {
          comp.type = passwordType ? "password" : "username";
          if (loginType && cipher.login.hasUris && cipher.login.uris[0].hostname != null) {
            comp.usernameWebsite = cipher.login.uris[0].hostname;
          }
        }
      }
    );
    this.modal = modal;

    // eslint-disable-next-line rxjs-angular/prefer-takeuntil
    childComponent.onSelected.subscribe((value: string) => {
      this.modal.close();
      if (loginType) {
        this.addEditComponent.markPasswordAsDirty();
        if (passwordType) {
          this.addEditComponent.cipher.login.password = value;
        } else {
          this.addEditComponent.cipher.login.username = value;
        }
      }
    });

    // eslint-disable-next-line rxjs-angular/prefer-takeuntil
    this.modal.onClosed.subscribe(() => {
      this.modal = null;
    });
  }

  async addFolder() {
    this.messagingService.send("newFolder");
  }

  async editFolder(folderId: string) {
    if (this.modal != null) {
      this.modal.close();
    }

    const [modal, childComponent] = await this.modalService.openViewRef(
      FolderAddEditComponent,
      this.folderAddEditModalRef,
      (comp) => (comp.folderId = folderId)
    );
    this.modal = modal;

    // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
    childComponent.onSavedFolder.subscribe(async (folder: FolderView) => {
      this.modal.close();
      await this.vaultFilterComponent.reloadCollectionsAndFolders(this.activeFilter);
    });
    // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
    childComponent.onDeletedFolder.subscribe(async (folder: FolderView) => {
      this.modal.close();
      await this.vaultFilterComponent.reloadCollectionsAndFolders(this.activeFilter);
    });

    // eslint-disable-next-line rxjs-angular/prefer-takeuntil
    this.modal.onClosed.subscribe(() => {
      this.modal = null;
    });
  }

  private dirtyInput(): boolean {
    return (
      (this.action === "add" || this.action === "edit" || this.action === "clone") &&
      document.querySelectorAll("app-vault-add-edit .ng-dirty").length > 0
    );
  }

  private async wantsToSaveChanges(): Promise<boolean> {
    const confirmed = await this.dialogService.openSimpleDialog({
      title: { key: "unsavedChangesTitle" },
      content: { key: "unsavedChangesConfirmation" },
      type: SimpleDialogType.WARNING,
    });
    return !confirmed;
  }

  private go(queryParams: any = null) {
    if (queryParams == null) {
      queryParams = {
        action: this.action,
        cipherId: this.cipherId,
        favorites: this.favorites ? true : null,
        type: this.type,
        folderId: this.folderId,
        collectionId: this.collectionId,
        deleted: this.deleted ? true : null,
        organizationId: this.organizationId,
        myVaultOnly: this.myVaultOnly,
      };
    }

    this.router.navigate([], {
      relativeTo: this.route,
      queryParams: queryParams,
      replaceUrl: true,
    });
  }

  private addCipherWithChangeDetection(type: CipherType = null) {
    this.functionWithChangeDetection(() => this.addCipher(type));
  }

  private copyValue(cipher: CipherView, value: string, labelI18nKey: string, aType: string) {
    this.functionWithChangeDetection(async () => {
      if (
        cipher.reprompt !== CipherRepromptType.None &&
        this.passwordRepromptService.protectedFields().includes(aType) &&
        !(await this.passwordRepromptService.showPasswordPrompt())
      ) {
        return;
      }

      this.platformUtilsService.copyToClipboard(value);
      this.platformUtilsService.showToast(
        "info",
        null,
        this.i18nService.t("valueCopied", this.i18nService.t(labelI18nKey))
      );
      if (this.action === "view") {
        this.messagingService.send("minimizeOnCopy");
      }
    });
  }

  private functionWithChangeDetection(func: () => void) {
    this.ngZone.run(() => {
      func();
      this.changeDetectorRef.detectChanges();
    });
  }

  private prefillNewCipherFromFilter() {
    if (this.activeFilter.selectedCollectionId != null) {
      const collection = this.vaultFilterComponent.collections.fullList.filter(
        (c) => c.id === this.activeFilter.selectedCollectionId
      );
      if (collection.length > 0) {
        this.addOrganizationId = collection[0].organizationId;
        this.addCollectionIds = [this.activeFilter.selectedCollectionId];
      }
    } else if (this.activeFilter.selectedOrganizationId) {
      this.addOrganizationId = this.activeFilter.selectedOrganizationId;
    }
    if (this.activeFilter.selectedFolderId && this.activeFilter.selectedFolder) {
      this.folderId = this.activeFilter.selectedFolderId;
    }
  }

  private async canNavigateAway(action: string, cipher?: CipherView) {
    // Don't navigate to same route
    if (this.action === action && (cipher == null || this.cipherId === cipher.id)) {
      return false;
    } else if (this.dirtyInput() && (await this.wantsToSaveChanges())) {
      return false;
    }

    return true;
  }

  private async passwordReprompt(cipher: CipherView) {
    return (
      cipher.reprompt === CipherRepromptType.None ||
      (await this.passwordRepromptService.showPasswordPrompt())
    );
  }
}
