// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import "core-js/proposals/explicit-resource-management";

import {
  EMPTY,
  NEVER,
  Observable,
  Subject,
  concatMap,
  concat,
  filter,
  firstValueFrom,
  from,
  map,
  merge,
  of,
  switchMap,
  timeout,
} from "rxjs";

import {
  CollectionEncryptionService,
  CollectionService,
  DefaultCollectionEncryptionService,
  DefaultCollectionService,
  DefaultOrganizationUserApiService,
  DefaultOrganizationUserService,
  OrganizationUserApiService,
  OrganizationUserService,
} from "@bitwarden/admin-console/common";
import {
  AuthRequestApiServiceAbstraction,
  AuthRequestService,
  AuthRequestServiceAbstraction,
  DefaultAuthRequestApiService,
  DefaultLogoutService,
  InternalUserDecryptionOptionsServiceAbstraction,
  LockService,
  LoginEmailServiceAbstraction,
  DefaultLoginStrategyCacheService,
  DefaultLoginStrategySessionTimeoutService,
  LogoutReason,
  UserDecryptionOptionsService,
} from "@bitwarden/auth/common";
import {
  AutomaticUserConfirmationService,
  DefaultAutomaticUserConfirmationService,
} from "@bitwarden/auto-confirm";
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service";
import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { InternalNewPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/new-policy.service";
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
import { InternalPolicyService as InternalPolicyServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { ProviderService as ProviderServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider.service";
import { DefaultOrganizationService } from "@bitwarden/common/admin-console/services/organization/default-organization.service";
import { DefaultNewPolicyService } from "@bitwarden/common/admin-console/services/policy/default-new-policy.service";
import { DefaultPolicyService } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service";
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthRequestAnsweringService } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction";
import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification-api.service.abstraction";
import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AuthServerNotificationTags } from "@bitwarden/common/auth/enums/auth-server-notification-tags";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
import { PendingAuthRequestsStateService } from "@bitwarden/common/auth/services/auth-request-answering/pending-auth-requests.state";
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
import { DefaultActiveUserAccessor } from "@bitwarden/common/auth/services/default-active-user.accessor";
import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation";
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
import { SsoLoginService } from "@bitwarden/common/auth/services/sso-login/sso-login.service";
import { TokenService } from "@bitwarden/common/auth/services/token.service";
import { UserVerificationApiService } from "@bitwarden/common/auth/services/user-verification/user-verification-api.service";
import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.service";
import {
  AutofillSettingsService,
  AutofillSettingsServiceAbstraction,
} from "@bitwarden/common/autofill/services/autofill-settings.service";
import {
  BadgeSettingsService,
  BadgeSettingsServiceAbstraction,
} from "@bitwarden/common/autofill/services/badge-settings.service";
import {
  DefaultDomainSettingsService,
  DomainSettingsService,
} from "@bitwarden/common/autofill/services/domain-settings.service";
import {
  UserNotificationSettingsService,
  UserNotificationSettingsServiceAbstraction,
} from "@bitwarden/common/autofill/services/user-notification-settings.service";
import { isUrlInList } from "@bitwarden/common/autofill/utils";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service";
import {
  EventUploadService as EventUploadServiceAbstraction,
  EventCollectionService as EventCollectionServiceAbstraction,
} from "@bitwarden/common/dirt/event-logs";
import { EventCollectionService } from "@bitwarden/common/dirt/event-logs/services/event-collection.service";
import { EventUploadService } from "@bitwarden/common/dirt/event-logs/services/event-upload.service";
import { PhishingDetectionSettingsServiceAbstraction } from "@bitwarden/common/dirt/services/abstractions/phishing-detection-settings.service.abstraction";
import { HibpApiService } from "@bitwarden/common/dirt/services/hibp-api.service";
import { PhishingDetectionSettingsService } from "@bitwarden/common/dirt/services/phishing-detection/phishing-detection-settings.service";
import { ClientType } from "@bitwarden/common/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service";
import { AccountCryptographicStateService } from "@bitwarden/common/key-management/account-cryptography/account-cryptographic-state.service";
import { DefaultAccountCryptographicStateService } from "@bitwarden/common/key-management/account-cryptography/default-account-cryptographic-state.service";
import {
  DefaultKeyGenerationService,
  KeyGenerationService,
} from "@bitwarden/common/key-management/crypto";
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { EncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/encrypt.service.implementation";
import { WebCryptoFunctionService } from "@bitwarden/common/key-management/crypto/services/web-crypto-function.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { DeviceTrustService } from "@bitwarden/common/key-management/device-trust/services/device-trust.service.implementation";
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/services/key-connector.service";
import { MasterPasswordUnlockService } from "@bitwarden/common/key-management/master-password/abstractions/master-password-unlock.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import { DefaultMasterPasswordUnlockService } from "@bitwarden/common/key-management/master-password/services/default-master-password-unlock.service";
import { MasterPasswordService } from "@bitwarden/common/key-management/master-password/services/master-password.service";
import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction";
import { PinService } from "@bitwarden/common/key-management/pin/pin.service.implementation";
import { DefaultProcessReloadService } from "@bitwarden/common/key-management/services/default-process-reload.service";
import {
  SharedUnlockLeaderService,
  SharedUnlockFollowerService,
  SharedUnlockSettingsService,
  DefaultSharedUnlockSettingsService,
} from "@bitwarden/common/key-management/shared-unlock";
import { DefaultSharedUnlockFollowerService } from "@bitwarden/common/key-management/shared-unlock/default-shared-unlock-follower.service";
import { DefaultSharedUnlockLeaderService } from "@bitwarden/common/key-management/shared-unlock/default-shared-unlock-leader.service";
import { V2UpgradeTokenStateService } from "@bitwarden/common/key-management/upgrade-token/abstractions/v2-upgrade-token-state.service.abstraction";
import { DefaultV2UpgradeTokenStateService } from "@bitwarden/common/key-management/upgrade-token/services/default-v2-upgrade-token-state.service";
import {
  DefaultVaultTimeoutSettingsService,
  VaultTimeoutSettingsService,
  VaultTimeoutStringType,
} from "@bitwarden/common/key-management/vault-timeout";
import {
  AnimationControlService,
  DefaultAnimationControlService,
} from "@bitwarden/common/platform/abstractions/animation-control.service";
import { AppIdService as AppIdServiceAbstraction } from "@bitwarden/common/platform/abstractions/app-id.service";
import { AvailableRegionsService } from "@bitwarden/common/platform/abstractions/available-regions.service";
import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { RegionConfig } from "@bitwarden/common/platform/abstractions/environment.service";
import { Fido2ActiveRequestManager as Fido2ActiveRequestManagerAbstraction } from "@bitwarden/common/platform/abstractions/fido2/fido2-active-request-manager.abstraction";
import { Fido2AuthenticatorService as Fido2AuthenticatorServiceAbstraction } from "@bitwarden/common/platform/abstractions/fido2/fido2-authenticator.service.abstraction";
import { Fido2ClientService as Fido2ClientServiceAbstraction } from "@bitwarden/common/platform/abstractions/fido2/fido2-client.service.abstraction";
import { Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction } from "@bitwarden/common/platform/abstractions/fido2/fido2-user-interface.service.abstraction";
import { FileUploadService as FileUploadServiceAbstraction } from "@bitwarden/common/platform/abstractions/file-upload/file-upload.service";
import { GovModeService } from "@bitwarden/common/platform/abstractions/gov-mode.service";
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { RegisterSdkService } from "@bitwarden/common/platform/abstractions/sdk/register-sdk.service";
import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service";
import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
import {
  AbstractStorageService,
  ObservableStorageService,
} from "@bitwarden/common/platform/abstractions/storage.service";
import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/platform/abstractions/system.service";
import { ActionsService } from "@bitwarden/common/platform/actions/actions-service";
import { IpcService } from "@bitwarden/common/platform/ipc";
import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging";
// eslint-disable-next-line no-restricted-imports -- Used for dependency creation
import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal";
import { ServerNotificationsService } from "@bitwarden/common/platform/server-notifications";
// eslint-disable-next-line no-restricted-imports -- Needed for service creation
import {
  DefaultServerNotificationsService,
  SignalRConnectionService,
  UnsupportedWebPushConnectionService,
  WebPushNotificationsApiService,
  WorkerWebPushConnectionService,
} from "@bitwarden/common/platform/server-notifications/internal";
import { AppIdService } from "@bitwarden/common/platform/services/app-id.service";
import { ConfigApiService } from "@bitwarden/common/platform/services/config/config-api.service";
import { DefaultConfigService } from "@bitwarden/common/platform/services/config/default-config.service";
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
import { DefaultAvailableRegionsService } from "@bitwarden/common/platform/services/default-available-regions.service";
import { DefaultGovModeService } from "@bitwarden/common/platform/services/default-gov-mode.service";
import { Fido2ActiveRequestManager } from "@bitwarden/common/platform/services/fido2/fido2-active-request-manager";
import { Fido2AuthenticatorService } from "@bitwarden/common/platform/services/fido2/fido2-authenticator.service";
import { Fido2ClientService } from "@bitwarden/common/platform/services/fido2/fido2-client.service";
import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.service";
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory";
import { DefaultSdkService } from "@bitwarden/common/platform/services/sdk/default-sdk.service";
import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory";
import { DefaultRegisterSdkService } from "@bitwarden/common/platform/services/sdk/register-sdk.service";
import { SystemService } from "@bitwarden/common/platform/services/system.service";
import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service";
import { PrimarySecondaryStorageService } from "@bitwarden/common/platform/storage/primary-secondary-storage.service";
import { WindowStorageService } from "@bitwarden/common/platform/storage/window-storage.service";
import { SyncService } from "@bitwarden/common/platform/sync";
// eslint-disable-next-line no-restricted-imports -- Needed for service creation
import { DefaultSyncService } from "@bitwarden/common/platform/sync/internal";
import { SystemNotificationsService } from "@bitwarden/common/platform/system-notifications/";
import { SystemNotificationEvent } from "@bitwarden/common/platform/system-notifications/system-notifications.service";
import { UnsupportedSystemNotificationsService } from "@bitwarden/common/platform/system-notifications/unsupported-system-notifications.service";
import { DefaultThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
import { ApiService } from "@bitwarden/common/services/api.service";
import { AuditService } from "@bitwarden/common/services/audit.service";
import { KeyServiceLegacyEncryptorProvider } from "@bitwarden/common/tools/cryptography/key-service-legacy-encryptor-provider";
import { buildExtensionRegistry } from "@bitwarden/common/tools/extension/factory";
import {
  PasswordStrengthService,
  PasswordStrengthServiceAbstraction,
} from "@bitwarden/common/tools/password-strength";
import { createSystemServiceProvider } from "@bitwarden/common/tools/providers";
import { SendApiServiceSelector } from "@bitwarden/common/tools/send/services/send-api-service.selector";
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service";
import { SendApiService as SendApiServiceAbstraction } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
import { SendSdkApiService } from "@bitwarden/common/tools/send/services/send-sdk-api.service";
import { SendStateProvider } from "@bitwarden/common/tools/send/services/send-state.provider";
import { SendService } from "@bitwarden/common/tools/send/services/send.service";
import { InternalSendService as InternalSendServiceAbstraction } from "@bitwarden/common/tools/send/services/send.service.abstraction";
import { UserId } from "@bitwarden/common/types/guid";
import { ChangeLoginPasswordService } from "@bitwarden/common/vault/abstractions/change-login-password.service";
import { CipherEncryptionService } from "@bitwarden/common/vault/abstractions/cipher-encryption.service";
import { CipherSdkService } from "@bitwarden/common/vault/abstractions/cipher-sdk.service";
import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherFileUploadService as CipherFileUploadServiceAbstraction } from "@bitwarden/common/vault/abstractions/file-upload/cipher-file-upload.service";
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
import { InternalFolderService as InternalFolderServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/vault/abstractions/search.service";
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service";
import { VaultSettingsService as VaultSettingsServiceAbstraction } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
import { ExtensionPageUrls } from "@bitwarden/common/vault/enums";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import {
  DefaultEndUserNotificationService,
  EndUserNotificationService,
} from "@bitwarden/common/vault/notifications";
import {
  CipherAuthorizationService,
  DefaultCipherAuthorizationService,
} from "@bitwarden/common/vault/services/cipher-authorization.service";
import { DefaultCipherSdkService } from "@bitwarden/common/vault/services/cipher-sdk.service";
import { CipherService } from "@bitwarden/common/vault/services/cipher.service";
import { DefaultChangeLoginPasswordService } from "@bitwarden/common/vault/services/default-change-login-password.service";
import { DefaultCipherEncryptionService } from "@bitwarden/common/vault/services/default-cipher-encryption.service";
import { CipherFileUploadService } from "@bitwarden/common/vault/services/file-upload/cipher-file-upload.service";
import { FolderApiService } from "@bitwarden/common/vault/services/folder/folder-api.service";
import { FolderService } from "@bitwarden/common/vault/services/folder/folder.service";
import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service";
import { SearchService } from "@bitwarden/common/vault/services/search.service";
import { TotpService } from "@bitwarden/common/vault/services/totp.service";
import { VaultSettingsService } from "@bitwarden/common/vault/services/vault-settings/vault-settings.service";
import { DefaultTaskService, TaskService } from "@bitwarden/common/vault/tasks";
import {
  createCredentialGeneratorService,
  createRandomizer,
  CredentialGeneratorService,
  Type,
} from "@bitwarden/generator-core";
import {
  GeneratorHistoryService,
  LocalGeneratorHistoryService,
} from "@bitwarden/generator-history";
import {
  legacyPasswordGenerationServiceFactory,
  legacyUsernameGenerationServiceFactory,
  PasswordGenerationServiceAbstraction,
  UsernameGenerationServiceAbstraction,
} from "@bitwarden/generator-legacy";
import {
  DefaultImportMetadataService,
  ImportApiService,
  ImportApiServiceAbstraction,
  ImportMetadataServiceAbstraction,
  ImportService,
  ImportServiceAbstraction,
} from "@bitwarden/importer-core";
import {
  BiometricStateService,
  DefaultBiometricStateService,
  DefaultKdfConfigService,
  DefaultKeyService,
  KdfConfigService,
  KeyService as KeyServiceAbstraction,
} from "@bitwarden/key-management";
import { BackgroundSyncService } from "@bitwarden/platform/background-sync";
import {
  ActiveUserStateProvider,
  DerivedStateProvider,
  GlobalStateProvider,
  SingleUserStateProvider,
  StateEventRunnerService,
  StateProvider,
} from "@bitwarden/state";
import {
  DefaultActiveUserStateProvider,
  DefaultGlobalStateProvider,
  DefaultSingleUserStateProvider,
  DefaultStateEventRegistrarService,
  DefaultStateEventRunnerService,
  DefaultStateProvider,
  DefaultStateService,
  InlineDerivedStateProvider,
} from "@bitwarden/state-internal";
import { DefaultUnlockService, UnlockService } from "@bitwarden/unlock";
import {
  IndividualVaultExportService,
  IndividualVaultExportServiceAbstraction,
  OrganizationVaultExportService,
  OrganizationVaultExportServiceAbstraction,
  DefaultVaultExportApiService,
  VaultExportApiService,
  VaultExportService,
  VaultExportServiceAbstraction,
} from "@bitwarden/vault-export-core";

import { ExtensionAuthRequestAnsweringService } from "../auth/services/auth-request-answering/extension-auth-request-answering.service";
import { AuthStatusBadgeUpdaterService } from "../auth/services/auth-status-badge-updater.service";
import { ExtensionLockService } from "../auth/services/extension-lock.service";
import { OverlayNotificationsBackground as OverlayNotificationsBackgroundInterface } from "../autofill/background/abstractions/overlay-notifications.background";
import {
  OverlayBackground as OverlayBackgroundInterface,
  PasswordGenerateRequestSource,
} from "../autofill/background/abstractions/overlay.background";
import { AutoSubmitLoginBackground } from "../autofill/background/auto-submit-login.background";
import { AutofillOrchestrator } from "../autofill/background/autofill-orchestrator";
import ContextMenusBackground from "../autofill/background/context-menus.background";
import NotificationBackground from "../autofill/background/notification.background";
import { OverlayNotificationsBackground } from "../autofill/background/overlay-notifications.background";
import { OverlayBackground } from "../autofill/background/overlay.background";
import TabsBackground from "../autofill/background/tabs.background";
import WebRequestBackground from "../autofill/background/web-request.background";
import { CipherContextMenuHandler } from "../autofill/browser/cipher-context-menu-handler";
import { ContextMenuClickedHandler } from "../autofill/browser/context-menu-clicked-handler";
import { MainContextMenuHandler } from "../autofill/browser/main-context-menu-handler";
import { DefaultPasswordManagerPromptStateAccessor } from "../autofill/default-password-manager-prompt-state.accessor";
import { Fido2Background as Fido2BackgroundAbstraction } from "../autofill/fido2/background/abstractions/fido2.background";
import { Fido2Background } from "../autofill/fido2/background/fido2.background";
import { PermissionsPolicyBackground } from "../autofill/fido2/background/permissions-policy/permissions-policy.background";
import {
  BrowserFido2ParentWindowReference,
  BrowserFido2UserInterfaceService,
} from "../autofill/fido2/services/browser-fido2-user-interface.service";
import { AutofillLifecycleService } from "../autofill/services/abstractions/autofill-lifecycle.service";
import { AutofillService as AutofillServiceAbstraction } from "../autofill/services/abstractions/autofill.service";
import { AutofillBadgeUpdaterService } from "../autofill/services/autofill-badge-updater.service";
import { DefaultAutofillLifecycleService } from "../autofill/services/autofill-lifecycle.service";
import { AutofillTriageService } from "../autofill/services/autofill-triage.service";
import AutofillService from "../autofill/services/autofill.service";
import { ClipboardNotificationBadgeUpdaterService } from "../autofill/services/clipboard-notification-badge-updater.service";
import { InlineMenuFieldQualificationService } from "../autofill/services/inline-menu-field-qualification.service";
import { TargetingRulesDataService } from "../autofill/services/targeting-rules-data.service";
import { trackGeneratedCredential } from "../autofill/utils/credential-history-utils";
import { SafariApp } from "../browser/safariApp";
import { PhishingDataService } from "../dirt/phishing-detection/services/phishing-data.service";
import { PhishingDetectionService } from "../dirt/phishing-detection/services/phishing-detection.service";
import { BackgroundBrowserBiometricsService } from "../key-management/biometrics/background-browser-biometrics.service";
import { BrowserSessionTimeoutTypeService } from "../key-management/session-timeout/services/browser-session-timeout-type.service";
import { SHARED_UNLOCK_EXTERNAL } from "../key-management/shared-unlock-messages";
import VaultTimeoutService from "../key-management/vault-timeout/vault-timeout.service";
import { BrowserActionsService } from "../platform/actions/browser-actions.service";
import { DefaultBadgeBrowserApi } from "../platform/badge/badge-browser-api";
import { BadgeService } from "../platform/badge/badge.service";
import { BrowserApi } from "../platform/browser/browser-api";
import BrowserPopupUtils from "../platform/browser/browser-popup-utils";
import { flagEnabled } from "../platform/flags";
import { IpcBackgroundService } from "../platform/ipc/ipc-background.service";
import { IpcContentScriptManagerService } from "../platform/ipc/ipc-content-script-manager.service";
/* eslint-disable no-restricted-imports */
import { ChromeMessageSender } from "../platform/messaging/chrome-message.sender";
/* eslint-enable no-restricted-imports */
import { OffscreenDocumentService } from "../platform/offscreen-document/abstractions/offscreen-document";
import { DefaultOffscreenDocumentService } from "../platform/offscreen-document/offscreen-document.service";
import { BrowserTaskSchedulerService } from "../platform/services/abstractions/browser-task-scheduler.service";
import { BrowserEnvironmentService } from "../platform/services/browser-environment.service";
import BrowserInitialInstallService from "../platform/services/browser-initial-install.service";
import BrowserLocalStorageService from "../platform/services/browser-local-storage.service";
import BrowserMemoryStorageService from "../platform/services/browser-memory-storage.service";
import { BrowserScriptInjectorService } from "../platform/services/browser-script-injector.service";
import I18nService from "../platform/services/i18n.service";
import { LocalBackedSessionStorageService } from "../platform/services/local-backed-session-storage.service";
import { BackgroundPlatformUtilsService } from "../platform/services/platform-utils/background-platform-utils.service";
import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service";
import { PopupRouterCacheBackgroundService } from "../platform/services/popup-router-cache-background.service";
import { PopupViewCacheBackgroundService } from "../platform/services/popup-view-cache-background.service";
import { BrowserSdkLoadService } from "../platform/services/sdk/browser-sdk-load.service";
import { BackgroundTaskSchedulerService } from "../platform/services/task-scheduler/background-task-scheduler.service";
import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service";
import { BrowserStorageServiceProvider } from "../platform/storage/browser-storage-service.provider";
import { OffscreenStorageService } from "../platform/storage/offscreen-storage.service";
import { SyncServiceListener } from "../platform/sync/sync-service.listener";
import {
  BrowserSystemNotificationService,
  isNotificationsSupported,
} from "../platform/system-notifications/browser-system-notification.service";
import { fromChromeRuntimeMessaging } from "../platform/utils/from-chrome-runtime-messaging";
import { AtRiskCipherBadgeUpdaterService } from "../vault/services/at-risk-cipher-badge-updater.service";

import CommandsBackground from "./commands.background";
import IdleBackground from "./idle.background";
import { NativeMessagingBackground } from "./nativeMessaging.background";
import RuntimeBackground from "./runtime.background";

export default class MainBackground {
  messagingService: MessageSender;
  storageService: BrowserLocalStorageService;
  secureStorageService: AbstractStorageService;
  memoryStorageService: AbstractStorageService;
  memoryStorageForStateProviders: AbstractStorageService & ObservableStorageService;
  largeObjectMemoryStorageForStateProviders: AbstractStorageService & ObservableStorageService;
  i18nService: I18nServiceAbstraction;
  platformUtilsService: PlatformUtilsServiceAbstraction;
  logService: LogServiceAbstraction;
  keyGenerationService: KeyGenerationService;
  keyService: KeyServiceAbstraction;
  cryptoFunctionService: CryptoFunctionServiceAbstraction;
  masterPasswordService: InternalMasterPasswordServiceAbstraction;
  masterPasswordUnlockService: MasterPasswordUnlockService;
  tokenService: TokenServiceAbstraction;
  appIdService: AppIdServiceAbstraction;
  apiService: ApiServiceAbstraction;
  hibpApiService: HibpApiService;
  environmentService: BrowserEnvironmentService;
  cipherSdkService: CipherSdkService;
  cipherService: CipherServiceAbstraction;
  folderService: InternalFolderServiceAbstraction;
  userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction;
  collectionService: CollectionService;
  lockService: LockService;
  vaultTimeoutService?: VaultTimeoutService;
  vaultTimeoutSettingsService: VaultTimeoutSettingsService;
  passwordGenerationService: PasswordGenerationServiceAbstraction;
  credentialGeneratorService: CredentialGeneratorService;
  generatorHistoryService: GeneratorHistoryService;
  syncService: SyncService;
  passwordStrengthService: PasswordStrengthServiceAbstraction;
  totpService: TotpServiceAbstraction;
  autofillLifecycleService: AutofillLifecycleService;
  autofillService: AutofillServiceAbstraction;
  autofillOrchestrator: AutofillOrchestrator;
  containerService: ContainerService;
  auditService: AuditServiceAbstraction;
  authService: AuthServiceAbstraction;
  loginEmailService: LoginEmailServiceAbstraction;
  importApiService: ImportApiServiceAbstraction;
  importMetadataService: ImportMetadataServiceAbstraction;
  importService: ImportServiceAbstraction;
  exportApiService: VaultExportApiService;
  exportService: VaultExportServiceAbstraction;
  searchService: SearchServiceAbstraction;
  serverNotificationsService: ServerNotificationsService;
  systemNotificationService: SystemNotificationsService;
  actionsService: ActionsService;
  authRequestAnsweringService: AuthRequestAnsweringService;
  stateService: StateServiceAbstraction;
  userNotificationSettingsService: UserNotificationSettingsServiceAbstraction;
  autofillSettingsService: AutofillSettingsServiceAbstraction;
  badgeSettingsService: BadgeSettingsServiceAbstraction;
  domainSettingsService: DomainSettingsService;
  systemService: SystemServiceAbstraction;
  processReloadService: ProcessReloadServiceAbstraction;
  eventCollectionService: EventCollectionServiceAbstraction;
  eventUploadService: EventUploadServiceAbstraction;
  policyService: InternalPolicyServiceAbstraction;
  newPolicyService: InternalNewPolicyService;
  sendService: InternalSendServiceAbstraction;
  sendStateProvider: SendStateProvider;
  fileUploadService: FileUploadServiceAbstraction;
  cipherFileUploadService: CipherFileUploadServiceAbstraction;
  organizationService: InternalOrganizationServiceAbstraction;
  providerService: ProviderServiceAbstraction;
  keyConnectorService: KeyConnectorServiceAbstraction;
  userVerificationService: UserVerificationServiceAbstraction;
  usernameGenerationService: UsernameGenerationServiceAbstraction;
  encryptService: EncryptService;
  folderApiService: FolderApiServiceAbstraction;
  policyApiService: PolicyApiServiceAbstraction;
  sendApiService: SendApiServiceAbstraction;
  userVerificationApiService: UserVerificationApiServiceAbstraction;
  fido2UserInterfaceService: Fido2UserInterfaceServiceAbstraction<BrowserFido2ParentWindowReference>;
  fido2AuthenticatorService: Fido2AuthenticatorServiceAbstraction<BrowserFido2ParentWindowReference>;
  fido2ActiveRequestManager: Fido2ActiveRequestManagerAbstraction;
  fido2ClientService: Fido2ClientServiceAbstraction<BrowserFido2ParentWindowReference>;
  avatarService: AvatarServiceAbstraction;
  mainContextMenuHandler: MainContextMenuHandler;
  cipherContextMenuHandler: CipherContextMenuHandler;
  configService: ConfigService;
  availableRegionsService: AvailableRegionsService;
  govModeService: GovModeService;
  configApiService: ConfigApiServiceAbstraction;
  devicesApiService: DevicesApiServiceAbstraction;
  devicesService: DevicesServiceAbstraction;
  deviceTrustService: DeviceTrustServiceAbstraction;
  authRequestService: AuthRequestServiceAbstraction;
  authRequestApiService: AuthRequestApiServiceAbstraction;
  accountService: AccountServiceAbstraction;
  globalStateProvider: GlobalStateProvider;
  pinService: PinServiceAbstraction;
  singleUserStateProvider: SingleUserStateProvider;
  activeUserStateProvider: ActiveUserStateProvider;
  derivedStateProvider: DerivedStateProvider;
  stateProvider: StateProvider;
  migrationRunner: MigrationRunner;
  taskSchedulerService: BrowserTaskSchedulerService;
  fido2Background: Fido2BackgroundAbstraction;
  individualVaultExportService: IndividualVaultExportServiceAbstraction;
  organizationVaultExportService: OrganizationVaultExportServiceAbstraction;
  vaultSettingsService: VaultSettingsServiceAbstraction;
  pendingAuthRequestStateService: PendingAuthRequestsStateService;
  biometricStateService: BiometricStateService;
  biometricsService: BackgroundBrowserBiometricsService;
  stateEventRunnerService: StateEventRunnerService;
  ssoLoginService: SsoLoginServiceAbstraction;
  billingAccountProfileStateService: BillingAccountProfileStateService;
  // eslint-disable-next-line rxjs/no-exposed-subjects -- Needed to give access to services module
  intraprocessMessagingSubject: Subject<Message<Record<string, unknown>>>;
  userAutoUnlockKeyService: UserAutoUnlockKeyService;
  scriptInjectorService: BrowserScriptInjectorService;
  kdfConfigService: KdfConfigService;
  offscreenDocumentService: OffscreenDocumentService;
  syncServiceListener: SyncServiceListener;
  browserInitialInstallService: BrowserInitialInstallService;
  defaultPasswordManagerPromptStateAccessor: DefaultPasswordManagerPromptStateAccessor;
  backgroundSyncService: BackgroundSyncService;
  accountCryptographicStateService: AccountCryptographicStateService;
  v2UpgradeTokenStateService: V2UpgradeTokenStateService;

  webPushConnectionService: WorkerWebPushConnectionService | UnsupportedWebPushConnectionService;
  animationControlService: AnimationControlService;
  themeStateService: DefaultThemeStateService;
  autoSubmitLoginBackground: AutoSubmitLoginBackground;
  sdkService: SdkService;
  registerSdkService: RegisterSdkService;
  sdkLoadService: SdkLoadService;
  cipherAuthorizationService: CipherAuthorizationService;
  endUserNotificationService: EndUserNotificationService;
  inlineMenuFieldQualificationService: InlineMenuFieldQualificationService;
  autofillTriageService: AutofillTriageService;
  taskService: TaskService;
  cipherEncryptionService: CipherEncryptionService;
  collectionEncryptionService: CollectionEncryptionService;
  private restrictedItemTypesService: RestrictedItemTypesService;

  ipcContentScriptManagerService: IpcContentScriptManagerService;
  ipcService: IpcService;
  sharedUnlockLeaderService: SharedUnlockLeaderService;
  sharedUnlockFollowerService: SharedUnlockFollowerService;
  sharedUnlockSettingsService: SharedUnlockSettingsService;
  unlockService: UnlockService;

  badgeService: BadgeService;
  authStatusBadgeUpdaterService: AuthStatusBadgeUpdaterService;
  autofillBadgeUpdaterService: AutofillBadgeUpdaterService;
  clipboardNotificationBadgeUpdaterService: ClipboardNotificationBadgeUpdaterService;
  atRiskCipherUpdaterService: AtRiskCipherBadgeUpdaterService;

  changeLoginPasswordService: ChangeLoginPasswordService;

  onUpdatedRan: boolean;
  onReplacedRan: boolean;
  loginToAutoFill: CipherView = null;
  organizationUserService: OrganizationUserService;
  organizationUserApiService: OrganizationUserApiService;
  autoConfirmService: AutomaticUserConfirmationService;

  private commandsBackground: CommandsBackground;
  private contextMenusBackground: ContextMenusBackground;
  private idleBackground: IdleBackground;
  private notificationBackground: NotificationBackground;
  private overlayBackground: OverlayBackgroundInterface;
  private overlayNotificationsBackground: OverlayNotificationsBackgroundInterface;
  private runtimeBackground: RuntimeBackground;
  private tabsBackground: TabsBackground;
  private webRequestBackground: WebRequestBackground;

  private syncTimeout: any;
  private nativeMessagingBackground: NativeMessagingBackground;

  private popupViewCacheBackgroundService: PopupViewCacheBackgroundService;
  private popupRouterCacheBackgroundService: PopupRouterCacheBackgroundService;

  targetingRulesDataService: TargetingRulesDataService;

  // DIRT
  private phishingDataService: PhishingDataService;
  private phishingDetectionSettingsService: PhishingDetectionSettingsServiceAbstraction;
  private phishingDetectionService: PhishingDetectionService;

  constructor() {
    const logoutCallback = async (logoutReason: LogoutReason, userId?: UserId) =>
      await this.logout(logoutReason, userId);

    const runtimeNativeMessagingBackground = () => this.nativeMessagingBackground;

    const refreshAccessTokenErrorCallback = () => {
      // Send toast to popup
      this.messagingService.send("showToast", {
        type: "error",
        title: this.i18nService.t("errorRefreshingAccessToken"),
        message: this.i18nService.t("errorRefreshingAccessTokenDesc"),
      });
    };

    const isDev = process.env.ENV === "development";
    this.logService = new ConsoleLogService(isDev);
    this.cryptoFunctionService = new WebCryptoFunctionService(self);
    this.keyGenerationService = new DefaultKeyGenerationService(this.cryptoFunctionService);
    this.storageService = new BrowserLocalStorageService(this.logService);

    this.intraprocessMessagingSubject = new Subject<Message<Record<string, unknown>>>();

    this.messagingService = MessageSender.combine(
      new SubjectMessageSender(this.intraprocessMessagingSubject),
      new ChromeMessageSender(this.logService),
    );

    const messageListener = new MessageListener(
      merge(
        this.intraprocessMessagingSubject.asObservable(), // For messages from the same context
        fromChromeRuntimeMessaging(), // For messages from other contexts
      ),
    );

    this.offscreenDocumentService = new DefaultOffscreenDocumentService(this.logService);

    this.platformUtilsService = new BackgroundPlatformUtilsService(
      this.messagingService,
      (clipboardValue, clearMs) => this.clearClipboard(clipboardValue, clearMs),
      self,
      this.offscreenDocumentService,
    );

    this.secureStorageService = this.storageService; // secure storage is not supported in browsers, so we use local storage and warn users when it is used

    if (BrowserApi.isManifestVersion(3)) {
      // manifest v3 can reuse the same storage. They are split for v2 due to lacking a good sync mechanism, which isn't true for v3
      this.memoryStorageForStateProviders = new BrowserMemoryStorageService(); // mv3 stores to storage.session
      this.memoryStorageService = this.memoryStorageForStateProviders;
    } else {
      this.memoryStorageForStateProviders = new BackgroundMemoryStorageService(this.logService); // mv2 stores to memory
      this.memoryStorageService = this.memoryStorageForStateProviders;
    }

    this.encryptService = new EncryptServiceImplementation(
      this.cryptoFunctionService,
      this.logService,
      true,
    );

    if (BrowserApi.isManifestVersion(3)) {
      this.largeObjectMemoryStorageForStateProviders = new LocalBackedSessionStorageService(
        new BrowserMemoryStorageService(),
        this.storageService,
        this.keyGenerationService,
        this.encryptService,
        this.platformUtilsService,
        this.logService,
      );
    } else {
      // mv2 stores to the same location
      this.largeObjectMemoryStorageForStateProviders = this.memoryStorageForStateProviders;
    }

    const localStorageStorageService = BrowserApi.isManifestVersion(3)
      ? new OffscreenStorageService(this.offscreenDocumentService)
      : new WindowStorageService(self.localStorage);

    const storageServiceProvider = new BrowserStorageServiceProvider(
      this.storageService,
      this.memoryStorageForStateProviders,
      this.largeObjectMemoryStorageForStateProviders,
      new PrimarySecondaryStorageService(this.storageService, localStorageStorageService),
    );

    this.globalStateProvider = new DefaultGlobalStateProvider(
      storageServiceProvider,
      this.logService,
    );

    const stateEventRegistrarService = new DefaultStateEventRegistrarService(
      this.globalStateProvider,
      storageServiceProvider,
    );

    this.stateEventRunnerService = new DefaultStateEventRunnerService(
      this.globalStateProvider,
      storageServiceProvider,
    );

    this.singleUserStateProvider = new DefaultSingleUserStateProvider(
      storageServiceProvider,
      stateEventRegistrarService,
      this.logService,
    );
    this.accountService = new AccountServiceImplementation(
      this.messagingService,
      this.logService,
      this.globalStateProvider,
      this.singleUserStateProvider,
    );
    const activeUserAccessor = new DefaultActiveUserAccessor(this.accountService);
    this.activeUserStateProvider = new DefaultActiveUserStateProvider(
      activeUserAccessor,
      this.singleUserStateProvider,
    );
    this.derivedStateProvider = new InlineDerivedStateProvider();
    this.stateProvider = new DefaultStateProvider(
      this.activeUserStateProvider,
      this.singleUserStateProvider,
      this.globalStateProvider,
      this.derivedStateProvider,
    );

    this.taskSchedulerService = new BackgroundTaskSchedulerService(
      this.logService,
      this.stateProvider,
    );

    this.accountCryptographicStateService = new DefaultAccountCryptographicStateService(
      this.stateProvider,
    );

    this.v2UpgradeTokenStateService = new DefaultV2UpgradeTokenStateService(this.stateProvider);

    this.backgroundSyncService = new BackgroundSyncService(this.taskSchedulerService);
    this.backgroundSyncService.register(() => this.fullSync());

    this.environmentService = new BrowserEnvironmentService(
      this.logService,
      this.stateProvider,
      this.accountService,
      process.env.ADDITIONAL_REGIONS as unknown as RegionConfig[],
    );
    this.biometricStateService = new DefaultBiometricStateService(this.stateProvider);

    this.userNotificationSettingsService = new UserNotificationSettingsService(this.stateProvider);

    this.tokenService = new TokenService(
      this.singleUserStateProvider,
      this.globalStateProvider,
      this.platformUtilsService.supportsSecureStorage(),
      this.secureStorageService,
      this.encryptService,
      this.logService,
      logoutCallback,
    );

    this.popupViewCacheBackgroundService = new PopupViewCacheBackgroundService(
      messageListener,
      this.globalStateProvider,
      this.taskSchedulerService,
    );
    this.popupRouterCacheBackgroundService = new PopupRouterCacheBackgroundService(
      this.globalStateProvider,
    );

    this.migrationRunner = new MigrationRunner(
      this.storageService,
      this.logService,
      new MigrationBuilderService(),
      ClientType.Browser,
    );

    this.stateService = new DefaultStateService(
      this.storageService,
      this.secureStorageService,
      activeUserAccessor,
    );

    this.masterPasswordService = new MasterPasswordService(
      this.stateProvider,
      this.keyGenerationService,
      this.logService,
      this.cryptoFunctionService,
      this.accountService,
    );

    this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);

    this.kdfConfigService = new DefaultKdfConfigService(this.stateProvider);

    this.keyService = new DefaultKeyService(
      this.masterPasswordService,
      this.keyGenerationService,
      this.cryptoFunctionService,
      this.encryptService,
      this.platformUtilsService,
      this.logService,
      this.stateService,
      this.accountService,
      this.stateProvider,
      this.kdfConfigService,
      this.accountCryptographicStateService,
    );

    this.masterPasswordUnlockService = new DefaultMasterPasswordUnlockService(
      this.masterPasswordService,
      this.keyService,
      this.logService,
    );

    this.appIdService = new AppIdService(this.storageService, this.logService);

    this.userDecryptionOptionsService = new UserDecryptionOptionsService(
      this.singleUserStateProvider,
    );
    this.organizationService = new DefaultOrganizationService(this.stateProvider);

    this.newPolicyService = new DefaultNewPolicyService(this.stateProvider);

    this.policyService = new DefaultPolicyService(
      this.stateProvider,
      this.organizationService,
      this.accountService,
      this.newPolicyService,
      () => this.sdkService,
    );

    const sessionTimeoutTypeService = new BrowserSessionTimeoutTypeService(
      this.platformUtilsService,
    );

    this.vaultTimeoutSettingsService = new DefaultVaultTimeoutSettingsService(
      this.accountService,
      this.userDecryptionOptionsService,
      this.keyService,
      this.tokenService,
      this.policyService,
      this.biometricStateService,
      this.stateProvider,
      this.logService,
      VaultTimeoutStringType.OnRestart, // default vault timeout
      sessionTimeoutTypeService,
    );

    this.apiService = new ApiService(
      this.tokenService,
      this.platformUtilsService,
      this.environmentService,
      this.appIdService,
      refreshAccessTokenErrorCallback,
      this.logService,
      (logoutReason: LogoutReason, userId?: UserId) => this.logout(logoutReason, userId),
      this.vaultTimeoutSettingsService,
      this.accountService,
      { createRequest: (url, request) => new Request(url, request) },
    );

    this.organizationUserApiService = new DefaultOrganizationUserApiService(this.apiService);
    this.organizationUserService = new DefaultOrganizationUserService(
      this.keyService,
      this.encryptService,
      this.organizationUserApiService,
      this.accountService,
      this.i18nService,
    );

    this.hibpApiService = new HibpApiService(this.apiService);
    this.fileUploadService = new FileUploadService(
      this.logService,
      this.apiService,
      this.configService,
    );
    this.searchService = new SearchService(this.logService, this.i18nService);

    this.badgeSettingsService = new BadgeSettingsService(this.stateProvider);
    this.policyApiService = new PolicyApiService(
      this.policyService,
      this.newPolicyService,
      this.apiService,
      this.accountService,
    );

    this.authService = new AuthService(
      this.accountService,
      this.messagingService,
      this.keyService,
      this.apiService,
      this.stateService,
      this.tokenService,
    );

    this.configApiService = new ConfigApiService(this.apiService);

    this.configService = new DefaultConfigService(
      this.configApiService,
      this.environmentService,
      this.logService,
      this.stateProvider,
      this.authService,
    );

    this.availableRegionsService = new DefaultAvailableRegionsService(
      this.environmentService,
      this.configService,
    );

    this.govModeService = new DefaultGovModeService(this.environmentService);

    this.autoConfirmService = new DefaultAutomaticUserConfirmationService(
      this.apiService,
      this.organizationUserService,
      this.stateProvider,
      this.organizationService,
      this.organizationUserApiService,
      this.policyService,
      this.authService,
      this.accountService,
    );

    const sdkClientFactory = flagEnabled("sdk")
      ? new DefaultSdkClientFactory()
      : new NoopSdkClientFactory();
    this.sdkLoadService = new BrowserSdkLoadService(this.logService);
    this.sdkService = new DefaultSdkService(
      sdkClientFactory,
      this.environmentService,
      this.platformUtilsService,
      this.accountService,
      this.kdfConfigService,
      this.keyService,
      this.accountCryptographicStateService,
      this.apiService,
      this.stateProvider,
      this.configService,
      this.v2UpgradeTokenStateService,
    );

    this.registerSdkService = new DefaultRegisterSdkService(
      sdkClientFactory,
      this.environmentService,
      this.platformUtilsService,
      this.accountService,
      this.apiService,
      this.stateProvider,
      this.configService,
    );

    this.collectionEncryptionService = new DefaultCollectionEncryptionService(
      this.sdkService,
      this.logService,
    );

    this.collectionService = new DefaultCollectionService(
      this.keyService,
      this.encryptService,
      this.i18nService,
      this.stateProvider,
      this.collectionEncryptionService,
    );

    this.keyConnectorService = new KeyConnectorService(
      this.accountService,
      this.masterPasswordService,
      this.keyService,
      this.apiService,
      this.tokenService,
      this.logService,
      this.organizationService,
      logoutCallback,
      this.stateProvider,
      this.configService,
      this.registerSdkService,
      this.accountCryptographicStateService,
      this.sdkService,
      this.userDecryptionOptionsService,
    );

    this.pinService = new PinService(this.sdkService);

    this.ipcContentScriptManagerService = new IpcContentScriptManagerService(this.configService);
    this.ipcService = new IpcBackgroundService(this.platformUtilsService, this.logService);

    const browserBiometricsService = new BackgroundBrowserBiometricsService(
      runtimeNativeMessagingBackground,
      () => this.configService,
      this.logService,
      this.keyService,
      this.biometricStateService,
      this.messagingService,
      this.vaultTimeoutSettingsService,
      () => this.ipcService,
    );
    // Temporary dependency cycle workaround, until browser biometrics is replaced by shared unlock
    this.biometricsService = browserBiometricsService;
    this.unlockService = new DefaultUnlockService(
      this.registerSdkService,
      this.accountCryptographicStateService,
      this.kdfConfigService,
      this.accountService,
      this.masterPasswordService,
      this.stateProvider,
      this.logService,
      this.biometricsService,
      this.platformUtilsService,
      this.stateService,
      this.biometricStateService,
      this.v2UpgradeTokenStateService,
    );
    void browserBiometricsService.setUnlockService(this.unlockService);

    this.passwordStrengthService = new PasswordStrengthService();

    this.passwordGenerationService = legacyPasswordGenerationServiceFactory(
      this.policyService,
      this.accountService,
      this.stateProvider,
      this.sdkService,
    );

    this.devicesApiService = new DevicesApiServiceImplementation(this.apiService);
    this.deviceTrustService = new DeviceTrustService(
      this.cryptoFunctionService,
      this.keyService,
      this.encryptService,
      this.appIdService,
      this.devicesApiService,
      this.i18nService,
      this.platformUtilsService,
      this.stateProvider,
      this.secureStorageService,
      this.userDecryptionOptionsService,
      this.logService,
      this.configService,
      this.accountService,
    );

    this.devicesService = new DevicesServiceImplementation(
      this.appIdService,
      this.devicesApiService,
      this.i18nService,
    );

    this.authRequestApiService = new DefaultAuthRequestApiService(this.apiService, this.logService);

    this.authRequestService = new AuthRequestService(
      this.appIdService,
      this.masterPasswordService,
      this.keyService,
      this.encryptService,
      this.apiService,
      this.stateProvider,
      this.authRequestApiService,
      this.accountService,
    );

    // Instantiated for its constructor side-effect: registers the login session timeout
    // task handler with the task scheduler in the background
    new DefaultLoginStrategySessionTimeoutService(
      this.taskSchedulerService,
      new DefaultLoginStrategyCacheService(this.globalStateProvider),
      this.logService,
      this.messagingService,
      messageListener,
    );
    this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService(
      this.stateProvider,
    );

    this.restrictedItemTypesService = new RestrictedItemTypesService(
      this.accountService,
      this.organizationService,
      this.policyService,
    );

    this.autofillSettingsService = new AutofillSettingsService(
      this.stateProvider,
      this.policyService,
      this.accountService,
      this.restrictedItemTypesService,
    );

    this.ssoLoginService = new SsoLoginService(
      this.stateProvider,
      this.logService,
      this.policyService,
      this.environmentService,
    );

    this.userVerificationApiService = new UserVerificationApiService(this.apiService);

    this.domainSettingsService = new DefaultDomainSettingsService(
      this.stateProvider,
      this.policyService,
      this.accountService,
      this.configService,
      this.environmentService,
      this.authService,
    );

    this.targetingRulesDataService = new TargetingRulesDataService(
      this.apiService,
      this.domainSettingsService,
      this.configService,
      this.environmentService,
      this.taskSchedulerService,
      this.globalStateProvider,
      this.logService,
    );

    this.themeStateService = new DefaultThemeStateService(this.globalStateProvider);
    this.animationControlService = new DefaultAnimationControlService(this.globalStateProvider);

    this.cipherEncryptionService = new DefaultCipherEncryptionService(
      this.sdkService,
      this.logService,
    );

    this.cipherSdkService = new DefaultCipherSdkService(this.sdkService, this.logService);

    this.cipherFileUploadService = new CipherFileUploadService(
      this.apiService,
      this.fileUploadService,
      this.configService,
      this.cipherSdkService,
    );

    this.cipherService = new CipherService(
      this.keyService,
      this.domainSettingsService,
      this.apiService,
      this.i18nService,
      this.autofillSettingsService,
      this.encryptService,
      this.cipherFileUploadService,
      this.configService,
      this.stateProvider,
      this.accountService,
      this.logService,
      this.cipherEncryptionService,
      this.messagingService,
      this.cipherSdkService,
    );
    this.folderService = new FolderService(
      this.keyService,
      this.encryptService,
      this.i18nService,
      this.cipherService,
      this.stateProvider,
    );
    this.folderApiService = new FolderApiService(this.folderService, this.apiService);

    this.userVerificationService = new UserVerificationService(
      this.keyService,
      this.accountService,
      this.masterPasswordService,
      this.i18nService,
      this.userVerificationApiService,
      this.userDecryptionOptionsService,
      this.pinService,
      this.kdfConfigService,
      this.biometricsService,
      this.masterPasswordUnlockService,
    );

    this.vaultSettingsService = new VaultSettingsService(
      this.stateProvider,
      this.restrictedItemTypesService,
    );

    this.containerService = new ContainerService(this.keyService, this.encryptService);

    this.sendStateProvider = new SendStateProvider(this.stateProvider);
    this.sendService = new SendService(
      this.accountService,
      this.keyService,
      this.i18nService,
      this.keyGenerationService,
      this.sendStateProvider,
      this.encryptService,
      this.configService,
    );
    const legacySendApiService = new SendApiService(
      this.apiService,
      this.fileUploadService,
      this.sendService,
    );
    this.sendApiService = new SendApiServiceSelector(
      this.configService,
      legacySendApiService,
      new SendSdkApiService(
        this.sdkService,
        legacySendApiService,
        this.sendService,
        this.accountService,
        this.logService,
      ),
    );

    this.avatarService = new AvatarService(this.apiService, this.stateProvider);

    this.providerService = new ProviderService(this.stateProvider);

    this.syncService = new DefaultSyncService(
      this.masterPasswordService,
      this.accountService,
      this.apiService,
      this.domainSettingsService,
      this.folderService,
      this.cipherService,
      this.keyService,
      this.collectionService,
      this.messagingService,
      this.policyService,
      this.newPolicyService,
      this.sendService,
      this.logService,
      this.keyConnectorService,
      this.providerService,
      this.folderApiService,
      this.organizationService,
      this.sendApiService,
      this.userDecryptionOptionsService,
      this.avatarService,
      logoutCallback,
      this.billingAccountProfileStateService,
      this.tokenService,
      this.authService,
      this.stateProvider,
      this.configService,
      this.sdkService,
    );

    this.syncServiceListener = new SyncServiceListener(
      this.syncService,
      messageListener,
      this.messagingService,
      this.logService,
    );

    this.eventUploadService = new EventUploadService(
      this.apiService,
      this.stateProvider,
      this.logService,
      this.authService,
      this.taskSchedulerService,
    );
    this.eventCollectionService = new EventCollectionService(
      this.cipherService,
      this.stateProvider,
      this.organizationService,
      this.eventUploadService,
      this.authService,
      this.accountService,
    );
    this.totpService = new TotpService(this.sdkService);

    this.scriptInjectorService = new BrowserScriptInjectorService(
      this.domainSettingsService,
      this.platformUtilsService,
      this.logService,
    );
    this.autofillLifecycleService = new DefaultAutofillLifecycleService(
      this.authService,
      this.logService,
    );
    this.autofillService = new AutofillService(
      this.cipherService,
      this.autofillSettingsService,
      this.totpService,
      this.eventCollectionService,
      this.logService,
      this.domainSettingsService,
      this.userVerificationService,
      this.billingAccountProfileStateService,
      this.scriptInjectorService,
      this.accountService,
      this.authService,
      this.userNotificationSettingsService,
      messageListener,
      this.animationControlService,
      this.autofillLifecycleService,
    );
    this.autofillOrchestrator = new AutofillOrchestrator(
      this.autofillLifecycleService,
      this.autofillService,
      this.autofillSettingsService,
      this.accountService,
      this.platformUtilsService,
      () => this.updateOverlayCiphers(),
      this.logService,
    );
    this.auditService = new AuditService(
      this.cryptoFunctionService,
      this.apiService,
      this.hibpApiService,
    );

    this.importApiService = new ImportApiService(this.apiService);

    this.importMetadataService = new DefaultImportMetadataService(
      createSystemServiceProvider(
        new KeyServiceLegacyEncryptorProvider(
          this.encryptService,
          this.keyService,
          this.sdkService,
        ),
        this.stateProvider,
        this.policyService,
        buildExtensionRegistry(),
        this.logService,
        this.platformUtilsService,
        this.configService,
      ),
    );

    this.importService = new ImportService(
      this.cipherService,
      this.folderService,
      this.importApiService,
      this.i18nService,
      this.collectionService,
      this.keyService,
      this.encryptService,
      this.keyGenerationService,
      this.accountService,
      this.restrictedItemTypesService,
      this.sdkService,
    );

    this.individualVaultExportService = new IndividualVaultExportService(
      this.folderService,
      this.cipherService,
      this.keyGenerationService,
      this.keyService,
      this.encryptService,
      this.kdfConfigService,
      this.apiService,
      this.restrictedItemTypesService,
      this.logService,
    );

    this.exportApiService = new DefaultVaultExportApiService(this.apiService);

    this.organizationVaultExportService = new OrganizationVaultExportService(
      this.cipherService,
      this.exportApiService,
      this.keyGenerationService,
      this.keyService,
      this.encryptService,
      this.collectionService,
      this.kdfConfigService,
      this.restrictedItemTypesService,
    );

    this.exportService = new VaultExportService(
      this.individualVaultExportService,
      this.organizationVaultExportService,
      this.accountService,
    );

    this.browserInitialInstallService = new BrowserInitialInstallService(this.stateProvider);
    this.defaultPasswordManagerPromptStateAccessor = new DefaultPasswordManagerPromptStateAccessor(
      this.stateProvider,
    );

    if (BrowserApi.isManifestVersion(3)) {
      const registration = (self as unknown as { registration: ServiceWorkerRegistration })
        ?.registration;

      if (registration != null) {
        this.webPushConnectionService = new WorkerWebPushConnectionService(
          this.configService,
          new WebPushNotificationsApiService(this.apiService, this.appIdService),
          registration,
          this.stateProvider,
        );
      } else {
        this.webPushConnectionService = new UnsupportedWebPushConnectionService();
      }
    } else {
      this.webPushConnectionService = new UnsupportedWebPushConnectionService();
    }

    this.actionsService = new BrowserActionsService(this.logService, this.platformUtilsService);

    if (isNotificationsSupported()) {
      this.systemNotificationService = new BrowserSystemNotificationService(
        this.platformUtilsService,
      );
    } else {
      this.systemNotificationService = new UnsupportedSystemNotificationsService();
    }

    this.pendingAuthRequestStateService = new PendingAuthRequestsStateService(this.stateProvider);

    this.authRequestAnsweringService = new ExtensionAuthRequestAnsweringService(
      this.accountService,
      this.authService,
      this.masterPasswordService,
      this.messagingService,
      this.pendingAuthRequestStateService,
      this.actionsService,
      this.i18nService,
      this.platformUtilsService,
      this.systemNotificationService,
      this.logService,
    );

    this.serverNotificationsService = new DefaultServerNotificationsService(
      this.logService,
      this.syncService,
      this.appIdService,
      this.environmentService,
      logoutCallback,
      this.messagingService,
      this.accountService,
      new SignalRConnectionService(this.apiService, this.logService, this.platformUtilsService),
      this.authService,
      this.webPushConnectionService,
      this.authRequestAnsweringService,
      this.configService,
      this.autoConfirmService,
      this.billingAccountProfileStateService,
    );

    this.fido2UserInterfaceService = new BrowserFido2UserInterfaceService(this.authService);
    this.fido2AuthenticatorService = new Fido2AuthenticatorService(
      this.cipherService,
      this.fido2UserInterfaceService,
      this.syncService,
      this.accountService,
      this.logService,
    );
    this.fido2ActiveRequestManager = new Fido2ActiveRequestManager();
    this.fido2ClientService = new Fido2ClientService(
      this.fido2AuthenticatorService,
      this.configService,
      this.authService,
      this.vaultSettingsService,
      this.domainSettingsService,
      this.taskSchedulerService,
      this.fido2ActiveRequestManager,
      this.logService,
    );

    const systemUtilsServiceReloadCallback = async () => {
      await this.taskSchedulerService.clearAllScheduledTasks();

      // Close browser action popup before reloading to prevent zombie popup with invalidated context.
      // The 'reloadProcess' message is sent by ProcessReloadService before this callback runs,
      // and popups will close themselves upon receiving it. Poll to verify popup is actually closed.
      await BrowserPopupUtils.waitForAllPopupsClose();

      BrowserApi.reloadExtension();
    };

    this.systemService = new SystemService(
      this.platformUtilsService,
      this.autofillSettingsService,
      this.taskSchedulerService,
    );

    this.processReloadService = new DefaultProcessReloadService(
      this.pinService,
      this.messagingService,
      systemUtilsServiceReloadCallback,
      this.vaultTimeoutSettingsService,
      this.biometricStateService,
      this.accountService,
      this.logService,
      this.authService,
    );

    // Background

    const permissionsPolicyBackground = new PermissionsPolicyBackground(
      chrome.webRequest,
      chrome.tabs,
      chrome.webNavigation,
      chrome.runtime,
    );
    this.fido2Background = new Fido2Background(
      this.logService,
      this.fido2ActiveRequestManager,
      this.fido2ClientService,
      this.vaultSettingsService,
      this.scriptInjectorService,
      this.authService,
      permissionsPolicyBackground,
    );

    const logoutService = new DefaultLogoutService(this.messagingService);
    this.lockService = new ExtensionLockService(
      this.accountService,
      this.biometricsService,
      this.vaultTimeoutSettingsService,
      logoutService,
      this.messagingService,
      this.searchService,
      this.folderService,
      this.masterPasswordService,
      this.stateEventRunnerService,
      this.cipherService,
      this.authService,
      this.systemService,
      this.processReloadService,
      this.logService,
      this.keyService,
      this,
    );

    this.vaultTimeoutService = new VaultTimeoutService(
      this.accountService,
      this.platformUtilsService,
      this.authService,
      this.vaultTimeoutSettingsService,
      this.taskSchedulerService,
      this.logService,
      this.lockService,
      logoutService,
    );

    this.runtimeBackground = new RuntimeBackground(
      this,
      this.autofillService,
      this.platformUtilsService as BrowserPlatformUtilsService,
      this.autofillSettingsService,
      this.processReloadService,
      this.environmentService,
      this.messagingService,
      this.logService,
      this.configService,
      messageListener,
      this.accountService,
      this.lockService,
      this.billingAccountProfileStateService,
      this.browserInitialInstallService,
      this.autofillLifecycleService,
      this.defaultPasswordManagerPromptStateAccessor,
      this.autofillOrchestrator,
    );
    this.nativeMessagingBackground = new NativeMessagingBackground(
      this.keyService,
      this.encryptService,
      this.cryptoFunctionService,
      this.messagingService,
      this.appIdService,
      this.platformUtilsService,
      this.logService,
      this.biometricStateService,
      this.accountService,
    );
    this.commandsBackground = new CommandsBackground(
      this,
      this.platformUtilsService,
      this.authService,
      () => this.generatePasswordToClipboard(),
      this.accountService,
      this.lockService,
    );

    this.taskService = new DefaultTaskService(
      this.stateProvider,
      this.apiService,
      this.organizationService,
      this.authService,
      this.serverNotificationsService,
      messageListener,
    );

    this.changeLoginPasswordService = new DefaultChangeLoginPasswordService(
      this.apiService,
      this.environmentService,
      this.domainSettingsService,
    );

    this.notificationBackground = new NotificationBackground(
      this.accountService,
      this.authService,
      this.autofillService,
      this.cipherService,
      this.collectionService,
      this.configService,
      this.domainSettingsService,
      this.environmentService,
      this.folderService,
      this.logService,
      this.organizationService,
      this.policyService,
      this.themeStateService,
      this.userNotificationSettingsService,
      this.taskService,
      this.changeLoginPasswordService,
      this.messagingService,
      this.fido2Background,
    );

    this.overlayNotificationsBackground = new OverlayNotificationsBackground(
      this.logService,
      this.notificationBackground,
    );

    this.autoSubmitLoginBackground = new AutoSubmitLoginBackground(
      this.logService,
      this.autofillService,
      this.scriptInjectorService,
      this.authService,
      this.platformUtilsService,
      this.policyService,
      this.accountService,
    );

    this.inlineMenuFieldQualificationService = new InlineMenuFieldQualificationService();
    this.autofillTriageService = new AutofillTriageService(
      this.inlineMenuFieldQualificationService,
    );

    const contextMenuClickedHandler = new ContextMenuClickedHandler(
      (options) => this.platformUtilsService.copyToClipboard(options.text),
      async (_tab) => {
        await firstValueFrom(this.generatePasswordToClipboard(), { defaultValue: undefined });
      },
      async (tab, cipher) => {
        this.loginToAutoFill = cipher;
        if (tab == null) {
          return;
        }

        // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
        // eslint-disable-next-line @typescript-eslint/no-floating-promises
        BrowserApi.tabSendMessage(tab, {
          command: "collectPageDetails",
          tab: tab,
          sender: "contextMenu",
        });
      },
      this.authService,
      this.cipherService,
      this.totpService,
      this.eventCollectionService,
      this.userVerificationService,
      this.accountService,
      this.autofillTriageService,
    );

    this.contextMenusBackground = new ContextMenusBackground(contextMenuClickedHandler);

    this.idleBackground = new IdleBackground(
      this.vaultTimeoutService,
      this.serverNotificationsService,
      this.accountService,
      this.vaultTimeoutSettingsService,
      this.lockService,
      logoutService,
    );

    this.usernameGenerationService = legacyUsernameGenerationServiceFactory(
      this.apiService,
      this.i18nService,
      this.keyService,
      this.sdkService,
      this.policyService,
      this.accountService,
      this.stateProvider,
    );

    this.mainContextMenuHandler = new MainContextMenuHandler(
      this.tokenService,
      this.autofillSettingsService,
      this.i18nService,
      this.logService,
      this.billingAccountProfileStateService,
      this.accountService,
      this.restrictedItemTypesService,
      this.configService,
    );

    this.cipherContextMenuHandler = new CipherContextMenuHandler(
      this.mainContextMenuHandler,
      this.authService,
      this.cipherService,
      this.accountService,
    );

    if (chrome.webRequest != null && chrome.webRequest.onAuthRequired != null) {
      this.webRequestBackground = new WebRequestBackground(
        this.platformUtilsService,
        this.cipherService,
        this.authService,
        this.accountService,
        chrome.webRequest,
      );
    }

    this.userAutoUnlockKeyService = new UserAutoUnlockKeyService(this.keyService);

    this.cipherAuthorizationService = new DefaultCipherAuthorizationService(
      this.collectionService,
      this.organizationService,
      this.accountService,
    );

    this.phishingDataService = new PhishingDataService(
      this.apiService,
      this.taskSchedulerService,
      this.globalStateProvider,
      this.logService,
      this.platformUtilsService,
    );

    this.phishingDetectionSettingsService = new PhishingDetectionSettingsService(
      this.accountService,
      this.billingAccountProfileStateService,
      this.configService,
      this.logService,
      this.organizationService,
      this.platformUtilsService,
      this.stateProvider,
    );

    this.phishingDetectionService = new PhishingDetectionService(
      this.logService,
      this.phishingDataService,
      this.phishingDetectionSettingsService,
      messageListener,
      this.eventCollectionService,
      this.organizationService,
      this.accountService,
    );

    this.sharedUnlockSettingsService = new DefaultSharedUnlockSettingsService(this.stateProvider);
    this.sharedUnlockLeaderService = new DefaultSharedUnlockLeaderService(
      this.ipcService,
      this.accountService,
      this.lockService,
      this.keyService,
      this.platformUtilsService,
      this.vaultTimeoutSettingsService,
      this.environmentService,
      this.sharedUnlockSettingsService,
      this.unlockService,
    );
    this.sharedUnlockFollowerService = new DefaultSharedUnlockFollowerService(
      this.ipcService,
      this.accountService,
      this.lockService,
      this.keyService,
      this.platformUtilsService,
      this.vaultTimeoutSettingsService,
      this.environmentService,
      this.sharedUnlockSettingsService,
      this.unlockService,
    );

    this.endUserNotificationService = new DefaultEndUserNotificationService(
      this.stateProvider,
      this.apiService,
      this.serverNotificationsService,
      this.authService,
      this.logService,
    );

    this.badgeService = new BadgeService(
      new DefaultBadgeBrowserApi(this.platformUtilsService),
      this.logService,
    );
    this.authStatusBadgeUpdaterService = new AuthStatusBadgeUpdaterService(
      this.badgeService,
      this.accountService,
      this.authService,
    );

    // Synchronous startup
    if (this.webPushConnectionService instanceof WorkerWebPushConnectionService) {
      this.webPushConnectionService.start();
    }

    // Putting this here so that all other services are initialized prior to trying to hook up
    // subscriptions to the notification chrome events.
    this.initNotificationSubscriptions();
  }

  async bootstrap() {
    this.containerService.attachToGlobal(self);

    await this.sdkLoadService.loadAndInit();
    // Only the "true" background should run migrations
    await this.migrationRunner.run();

    // This is here instead of in the InitService b/c we don't plan for
    // side effects to run in the Browser InitService.
    const accounts = await firstValueFrom(this.accountService.accounts$);
    const userIds = Object.keys(accounts) as UserId[];
    await this.tokenService.cleanupTokenStorage(userIds);

    const setUserKeyInMemoryPromises = [];
    for (const userId of userIds) {
      // For each acct, we must await the process of setting the user key in memory
      // if the auto user key is set to avoid race conditions of any code trying to access
      // the user key from mem.
      setUserKeyInMemoryPromises.push(
        this.userAutoUnlockKeyService.setUserKeyInMemoryIfAutoUserKeySet(userId),
      );
    }
    await Promise.all(setUserKeyInMemoryPromises);

    await (this.i18nService as I18nService).init();
    (this.eventUploadService as EventUploadService).init(true);

    this.popupViewCacheBackgroundService.startObservingMessages();
    this.popupRouterCacheBackgroundService.init();

    await this.vaultTimeoutService.init(true);
    this.fido2Background.init();
    // Wire the autofill lifecycle before runtime init triggers script
    // injection, so the onConnect listener is registered before any frame
    // connects.
    this.autofillLifecycleService.init();
    this.autofillOrchestrator.init();
    await this.runtimeBackground.init();
    await this.notificationBackground.init();
    this.overlayNotificationsBackground.init();
    this.commandsBackground.init();
    this.contextMenusBackground?.init();
    // Disable the side panel globally on startup so Bitwarden does not appear in
    // Chrome's side panel picker. It is enabled per-tab on demand when the user
    // triggers autofill triage via the context menu.
    if (BrowserApi.isSidePanelApiSupported) {
      await BrowserApi.setSidePanelOptions({ enabled: false });
    }
    this.idleBackground.init();
    this.webRequestBackground?.startListening();
    this.syncServiceListener?.listener$().subscribe();
    await this.autoSubmitLoginBackground.init();
    await this.targetingRulesDataService.init();

    // If the user is logged out, switch to the next account
    const active = await firstValueFrom(this.accountService.activeAccount$);
    if (active != null) {
      const authStatus = await firstValueFrom(
        this.authService.authStatuses$.pipe(map((statuses) => statuses[active.id])),
      );
      if (authStatus === AuthenticationStatus.LoggedOut) {
        const nextUpAccount = await firstValueFrom(this.accountService.nextUpAccount$);
        await this.switchAccount(nextUpAccount?.id);
      }
    }

    await this.initOverlayAndTabsBackground();
    await this.ipcContentScriptManagerService.init();
    await this.ipcService.init();
    if (await this.configService.getFeatureFlag(FeatureFlag.SharedUnlockPart1)) {
      await this.sharedUnlockLeaderService.start();
    }
    if (await this.configService.getFeatureFlag(FeatureFlag.SharedUnlockPart2)) {
      await this.sharedUnlockFollowerService.start();
      this.sharedUnlockFollowerService.externalUnlock$.subscribe((userId) => {
        this.messagingService.send(SHARED_UNLOCK_EXTERNAL, { userId });
      });
    }
    this.badgeService.startListening();

    return new Promise<void>((resolve) => {
      setTimeout(async () => {
        await this.fullSync(false);
        this.backgroundSyncService.init();
        this.serverNotificationsService.startListening();

        this.taskService.listenForTaskNotifications();
        this.endUserNotificationService.listenForEndUserNotifications();
        resolve();
      }, 500);
    });
  }

  async refreshMenu(forLocked = false) {
    if (!chrome.windows || !chrome.contextMenus) {
      return;
    }

    await MainContextMenuHandler.removeAll();

    if (forLocked) {
      await this.mainContextMenuHandler?.noAccess();
      this.onUpdatedRan = this.onReplacedRan = false;
      return;
    }

    const contextMenuIsEnabled = await this.mainContextMenuHandler?.init();
    if (!contextMenuIsEnabled) {
      this.onUpdatedRan = this.onReplacedRan = false;
      return;
    }

    const tab = await BrowserApi.getTabFromCurrentWindow();

    if (tab) {
      const currentUrlIsBlocked = await firstValueFrom(
        this.domainSettingsService.blockedInteractionsUris$.pipe(
          map((blockedInteractionsUrls) => {
            if (blockedInteractionsUrls && tab?.url?.length) {
              return isUrlInList(tab.url, blockedInteractionsUrls);
            }

            return false;
          }),
        ),
      );

      await this.cipherContextMenuHandler?.update(tab.url, currentUrlIsBlocked);
      this.onUpdatedRan = this.onReplacedRan = false;
    }
  }

  async updateOverlayCiphers() {
    // `overlayBackground` is null in popup only contexts
    if (this.overlayBackground) {
      await this.overlayBackground.updateOverlayCiphers();
    }
  }

  /**
   * Switch accounts to indicated userId -- null is no active user
   */
  async switchAccount(userId: UserId) {
    let nextAccountStatus: AuthenticationStatus;
    try {
      // HACK to ensure account is switched before proceeding
      const switchPromise = firstValueFrom(
        this.accountService.activeAccount$.pipe(
          filter((account) => (account?.id ?? null) === (userId ?? null)),
          timeout({
            first: 1_000,
            with: () => {
              throw new Error(
                "The account switch process did not complete in a reasonable amount of time.",
              );
            },
          }),
        ),
      );
      await this.popupViewCacheBackgroundService.clearState();
      await this.accountService.switchAccount(userId);
      await switchPromise;

      if (userId == null) {
        await this.refreshMenu();
        await this.updateOverlayCiphers();
        this.messagingService.send("goHome");
        return;
      }

      nextAccountStatus = await this.authService.getAuthStatus(userId);

      await this.systemService.clearPendingClipboard();

      if (nextAccountStatus === AuthenticationStatus.LoggedOut) {
        this.messagingService.send("goHome");
      } else if (nextAccountStatus === AuthenticationStatus.Locked) {
        this.messagingService.send("locked", { userId: userId });
      } else {
        this.messagingService.send("unlocked", { userId: userId });
        await this.refreshMenu();
        await this.updateOverlayCiphers();
        await this.syncService.fullSync(false);
      }
    } finally {
      this.messagingService.send("switchAccountFinish", {
        userId: userId,
        status: nextAccountStatus,
      });
    }
  }

  // TODO: PM-21212 - consolidate the logic of this method into the new ExtensionLogoutService
  async logout(logoutReason: LogoutReason, userId?: UserId) {
    const activeUserId = await firstValueFrom(
      this.accountService.activeAccount$.pipe(
        map((a) => a?.id),
        timeout({
          first: 2000,
          with: () => {
            throw new Error("No active account found to logout");
          },
        }),
      ),
    );

    const userBeingLoggedOut = userId ?? activeUserId;

    await this.eventUploadService.uploadEvents(userBeingLoggedOut);

    const newActiveUser =
      userBeingLoggedOut === activeUserId
        ? await firstValueFrom(this.accountService.nextUpAccount$.pipe(map((a) => a?.id)))
        : null;

    await this.switchAccount(newActiveUser);

    // HACK: We shouldn't wait for the authentication status to change but instead subscribe to the
    // authentication status to do various actions.
    const logoutPromise = firstValueFrom(
      this.authService.authStatusFor$(userBeingLoggedOut).pipe(
        filter((authenticationStatus) => authenticationStatus === AuthenticationStatus.LoggedOut),
        timeout({
          first: 5_000,
          with: () => {
            throw new Error("The logout process did not complete in a reasonable amount of time.");
          },
        }),
      ),
    );

    await Promise.all([
      this.cipherService.clear(userBeingLoggedOut),
      this.folderService.clear(userBeingLoggedOut),
      this.biometricStateService.logout(userBeingLoggedOut),
      this.popupViewCacheBackgroundService.clearState(),
      this.pinService.logout(userBeingLoggedOut),
      /* We intentionally do not clear:
       *  - autofillSettingsService
       *  - badgeSettingsService
       *  - userNotificationSettingsService
       */
    ]);

    await this.keyService.clearKeys(userBeingLoggedOut);

    //Needs to be checked before state is cleaned
    const needStorageReseed = await this.needsStorageReseed(userBeingLoggedOut);

    await this.stateService.clean({ userId: userBeingLoggedOut });
    await this.tokenService.clearTokens(userBeingLoggedOut);
    await this.accountService.clean(userBeingLoggedOut);

    await this.stateEventRunnerService.handleEvent("logout", userBeingLoggedOut);

    // HACK: Wait for the user logging outs authentication status to transition to LoggedOut
    await logoutPromise;

    this.messagingService.send("doneLoggingOut", {
      logoutReason: logoutReason,
      userId: userBeingLoggedOut,
    });

    if (needStorageReseed) {
      await this.reseedStorage();
    }

    if (BrowserApi.isManifestVersion(3)) {
      // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
      // eslint-disable-next-line @typescript-eslint/no-floating-promises
      BrowserApi.sendMessage("updateBadge");
    }
    await this.mainContextMenuHandler?.noAccess();
    await this.systemService.clearPendingClipboard();
    await this.processReloadService.startProcessReload();
  }

  private async needsStorageReseed(userId: UserId): Promise<boolean> {
    const currentVaultTimeout = await firstValueFrom(
      this.vaultTimeoutSettingsService.getVaultTimeoutByUserId$(userId),
    );
    return currentVaultTimeout == VaultTimeoutStringType.Never ? false : true;
  }

  async collectPageDetailsForContentScript(tab: any, sender: string, frameId: number = null) {
    if (tab == null || !tab.id) {
      return;
    }

    const options: any = {};
    if (frameId != null) {
      options.frameId = frameId;
    }

    // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
    // eslint-disable-next-line @typescript-eslint/no-floating-promises
    BrowserApi.tabSendMessage(
      tab,
      {
        command: "collectPageDetails",
        tab: tab,
        sender: sender,
      },
      options,
    );
  }

  /**
   * Opens the popup.
   *
   * @deprecated Migrating to the browser actions service.
   */
  async openPopup() {
    const browserAction = BrowserApi.getBrowserAction();

    if ("openPopup" in browserAction && typeof browserAction.openPopup === "function") {
      await browserAction.openPopup();
      return;
    }

    if (this.platformUtilsService.isSafari()) {
      await SafariApp.sendMessageToApp("showPopover", null, true);
    }
  }

  /** Opens the `/at-risk-passwords` page within the popup */
  async openAtRisksPasswordsPage() {
    const browserAction = BrowserApi.getBrowserAction();

    try {
      // Set route of the popup before attempting to open it.
      // If the vault is locked, this won't have an effect as the auth guards will
      // redirect the user to the login page.
      await browserAction.setPopup({ popup: ExtensionPageUrls.AtRiskPasswords });

      await this.openPopup();
    } finally {
      // Reset the popup route to the default route so any subsequent
      // popup openings will not open to the at-risk-passwords page.
      await browserAction.setPopup({
        popup: ExtensionPageUrls.Index,
      });
    }
  }

  /**
   * Opens the popup to the given page
   *
   * @default ExtensionPageUrls.Index
   * @deprecated Migrating to the browser actions service.
   */
  async openTheExtensionToPage(url: ExtensionPageUrls = ExtensionPageUrls.Index) {
    const isValidUrl = Object.values(ExtensionPageUrls).includes(url);

    // If a non-defined URL is provided, return early.
    if (!isValidUrl) {
      return;
    }

    const browserAction = BrowserApi.getBrowserAction();

    try {
      // Set route of the popup before attempting to open it.
      // If the vault is locked, this won't have an effect as the auth guards will
      // redirect the user to the login page.
      await browserAction.setPopup({ popup: url });

      await this.openPopup();
    } finally {
      // Reset the popup route to the default route so any subsequent
      // popup openings will not open to the at-risk-passwords page.
      await browserAction.setPopup({
        popup: ExtensionPageUrls.Index,
      });
    }
  }

  async reseedStorage() {
    if (
      !this.platformUtilsService.isChrome() &&
      !this.platformUtilsService.isVivaldi() &&
      !this.platformUtilsService.isOpera()
    ) {
      return;
    }

    await this.storageService.fillBuffer();
  }

  async clearClipboard(clipboardValue: string, clearMs: number) {
    if (this.systemService != null) {
      await this.systemService.clearClipboard(clipboardValue, clearMs);
    }
  }

  private async fullSync(override = false) {
    const syncInternal = 6 * 60 * 60 * 1000; // 6 hours
    const lastSync = await this.syncService.getLastSync();

    let lastSyncAgo = syncInternal + 1;
    if (lastSync != null) {
      lastSyncAgo = new Date().getTime() - lastSync.getTime();
    }

    if (override || lastSyncAgo >= syncInternal) {
      await this.syncService.fullSync(override);
    }
  }

  /**
   * This function is for creating any subscriptions for the background service worker. We do this
   * here because it's important to run this during the evaluation period of the browser extension
   * service worker. If it's not done this way we risk the service worker being closed before it's
   * registered these system notification click events.
   */
  initNotificationSubscriptions() {
    const handlers: Array<{
      startsWith: string;
      handler: (event: SystemNotificationEvent) => Promise<void>;
    }> = [];

    const register = (
      startsWith: string,
      handler: (event: SystemNotificationEvent) => Promise<void>,
    ) => {
      handlers.push({ startsWith, handler });
    };

    // ======= Register All System Notification Handlers Here =======
    register(AuthServerNotificationTags.AuthRequest, (event) =>
      this.authRequestAnsweringService.handleAuthRequestNotificationClicked(event),
    );
    // ======= End Register All System Notification Handlers =======

    const streams: Observable<void>[] = handlers.map(({ startsWith, handler }) =>
      this.systemNotificationService.notificationClicked$.pipe(
        filter((event: SystemNotificationEvent): boolean => event.id.startsWith(startsWith + "_")),
        switchMap((event: SystemNotificationEvent): Observable<void> =>
          from(Promise.resolve(handler(event))),
        ),
      ),
    );

    if (streams.length > 0) {
      merge(...streams).subscribe();
    }
  }

  /**
   * Temporary solution to handle initialization of the overlay background behind a feature flag.
   * Will be reverted to instantiation within the constructor once the feature flag is removed.
   */
  async initOverlayAndTabsBackground() {
    if (
      this.overlayBackground ||
      this.tabsBackground ||
      (await firstValueFrom(this.authService.activeAccountStatus$)) ===
        AuthenticationStatus.LoggedOut
    ) {
      return;
    }

    this.credentialGeneratorService = await createCredentialGeneratorService(
      createSystemServiceProvider(
        new KeyServiceLegacyEncryptorProvider(
          this.encryptService,
          this.keyService,
          this.sdkService,
        ),
        this.stateProvider,
        this.policyService,
        buildExtensionRegistry(),
        this.logService,
        this.platformUtilsService,
        this.configService,
      ),
      createRandomizer(),
      new KeyServiceLegacyEncryptorProvider(this.encryptService, this.keyService, this.sdkService),
      this.stateProvider,
      this.i18nService,
      this.apiService,
      this.sdkService,
    );

    // LocalGeneratorHistoryService is always the correct implementation for the
    // browser extension background — there is no feature-flag branching here unlike
    // createCredentialGeneratorService.
    this.generatorHistoryService = new LocalGeneratorHistoryService(
      this.stateProvider,
      this.sdkService,
    );

    this.overlayBackground = new OverlayBackground(
      this.logService,
      this.cipherService,
      this.autofillService,
      this.authService,
      this.environmentService,
      this.domainSettingsService,
      this.autofillSettingsService,
      this.i18nService,
      this.platformUtilsService,
      this.vaultSettingsService,
      this.fido2ActiveRequestManager,
      this.inlineMenuFieldQualificationService,
      this.themeStateService,
      this.totpService,
      this.accountService,
      this.generatorHistoryService,
      this.credentialGeneratorService,
    );

    this.autofillBadgeUpdaterService = new AutofillBadgeUpdaterService(
      this.badgeService,
      this.accountService,
      this.cipherService,
      this.badgeSettingsService,
      this.logService,
    );

    this.clipboardNotificationBadgeUpdaterService = new ClipboardNotificationBadgeUpdaterService(
      this.badgeService,
      this.accountService,
      this.autofillSettingsService,
    );

    this.atRiskCipherUpdaterService = new AtRiskCipherBadgeUpdaterService(
      this.badgeService,
      this.accountService,
      this.cipherService,
      this.taskService,
    );

    this.tabsBackground = new TabsBackground(
      this,
      this.notificationBackground,
      this.overlayBackground,
    );

    await this.overlayBackground.init();
    await this.tabsBackground.init();
    await this.autofillBadgeUpdaterService.init();
    await this.clipboardNotificationBadgeUpdaterService.init();
    await this.atRiskCipherUpdaterService.init();
  }

  generatePasswordToClipboard = () => {
    // FIXME: `credentialGeneratorService` and `generatorHistoryService` are initialized in
    // `initOverlayAndTabsBackground()`, which only runs for authenticated users. Returning EMPTY
    // here preserves the pre-migration behavior (unauthenticated users could not generate passwords
    // to clipboard), but this needs intentional assessment: should clipboard generation be allowed
    // without authentication?
    if (!this.credentialGeneratorService || !this.generatorHistoryService) {
      return EMPTY;
    }

    return this.credentialGeneratorService
      .generate$({
        on$: concat(
          of({ source: PasswordGenerateRequestSource.Clipboard, type: Type.password }),
          NEVER,
        ),
        account$: this.accountService.activeAccount$,
      })
      .pipe(
        concatMap(async (generated) => {
          this.platformUtilsService.copyToClipboard(generated.credential);
          try {
            await trackGeneratedCredential(
              this.generatorHistoryService,
              this.accountService.activeAccount$,
              generated,
            );
          } catch (e) {
            this.logService.error(e);
          }
          return generated.credential;
        }),
      );
  };
}
