// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import * as fs from "fs";
import * as path from "path";

import { firstValueFrom } from "rxjs";

import {
  CollectionEncryptionService,
  DefaultCollectionEncryptionService,
  DefaultCollectionService,
  DefaultOrganizationUserApiService,
  OrganizationUserApiService,
} from "@bitwarden/admin-console/common";
import {
  InternalUserDecryptionOptionsServiceAbstraction,
  AuthRequestService,
  DefaultLoginStrategyCacheService,
  LoginStrategyService,
  LoginStrategyServiceAbstraction,
  DefaultLoginStrategySessionTimeoutService,
  UserDecryptionOptionsService,
  SsoUrlService,
  AuthRequestApiServiceAbstraction,
  DefaultAuthRequestApiService,
  DefaultLockService,
  DefaultLogoutService,
  LockService,
} from "@bitwarden/auth/common";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.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 { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction";
import { DefaultOrganizationService } from "@bitwarden/common/admin-console/services/organization/default-organization.service";
import { OrganizationApiService } from "@bitwarden/common/admin-console/services/organization/organization-api.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 { ProviderApiService } from "@bitwarden/common/admin-console/services/provider/provider-api.service";
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { MasterPasswordApiService as MasterPasswordApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction";
import {
  DefaultPasswordPreloginService,
  PasswordPreloginApiService,
  PasswordPreloginService,
} from "@bitwarden/common/auth/password-prelogin";
import { SendTokenService, DefaultSendTokenService } from "@bitwarden/common/auth/send-access";
import {
  AccountServiceImplementation,
  getUserId,
} from "@bitwarden/common/auth/services/account.service";
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 { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
import { MasterPasswordApiService } from "@bitwarden/common/auth/services/master-password/master-password-api.service.implementation";
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 {
  DefaultTwoFactorService,
  TwoFactorService,
  TwoFactorApiService,
  DefaultTwoFactorApiService,
} from "@bitwarden/common/auth/two-factor";
import {
  AutofillSettingsService,
  AutofillSettingsServiceAbstraction,
} from "@bitwarden/common/autofill/services/autofill-settings.service";
import {
  DefaultDomainSettingsService,
  DomainSettingsService,
} from "@bitwarden/common/autofill/services/domain-settings.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { PremiumCheckoutPendingService } from "@bitwarden/common/billing/abstractions/account/premium-checkout-pending.service";
import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service";
import { DefaultPremiumCheckoutPendingService } from "@bitwarden/common/billing/services/account/default-premium-checkout-pending.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 { HibpApiService } from "@bitwarden/common/dirt/services/hibp-api.service";
import { ClientType } from "@bitwarden/common/enums";
import { DefaultAccountCryptographicStateService } from "@bitwarden/common/key-management/account-cryptography/default-account-cryptographic-state.service";
import {
  DefaultKeyGenerationService,
  KeyGenerationService,
} from "@bitwarden/common/key-management/crypto";
import { EncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/encrypt.service.implementation";
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 { DefaultEncryptedMigrator } from "@bitwarden/common/key-management/encrypted-migrator/default-encrypted-migrator";
import { EncryptedMigrator } from "@bitwarden/common/key-management/encrypted-migrator/encrypted-migrator.abstraction";
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 { SecurityStateService } from "@bitwarden/common/key-management/security-state/abstractions/security-state.service";
import { DefaultSecurityStateService } from "@bitwarden/common/key-management/security-state/services/security-state.service";
import { SendPasswordService } from "@bitwarden/common/key-management/sends/abstractions/send-password.service";
import { DefaultSendPasswordService } from "@bitwarden/common/key-management/sends/services/default-send-password.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 {
  DefaultVaultTimeoutService,
  DefaultVaultTimeoutSettingsService,
  VaultTimeoutService,
  VaultTimeoutSettingsService,
  VaultTimeoutStringType,
} from "@bitwarden/common/key-management/vault-timeout";
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 {
  EnvironmentService,
  RegionConfig,
} from "@bitwarden/common/platform/abstractions/environment.service";
import { GovModeService } from "@bitwarden/common/platform/abstractions/gov-mode.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 { LogLevelType } from "@bitwarden/common/platform/enums";
import { MessageListener, MessageSender } from "@bitwarden/common/platform/messaging";
import {
  TaskSchedulerService,
  DefaultTaskSchedulerService,
} from "@bitwarden/common/platform/scheduling";
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 { ContainerService } from "@bitwarden/common/platform/services/container.service";
import { DefaultAvailableRegionsService } from "@bitwarden/common/platform/services/default-available-regions.service";
import { DefaultEnvironmentService } from "@bitwarden/common/platform/services/default-environment.service";
import { DefaultGovModeService } from "@bitwarden/common/platform/services/default-gov-mode.service";
import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.service";
import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.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 { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider";
import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service";
import { SyncService } from "@bitwarden/common/platform/sync";
// eslint-disable-next-line no-restricted-imports -- Needed for service construction
import { DefaultSyncService } from "@bitwarden/common/platform/sync/internal";
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 { 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 { UserId } from "@bitwarden/common/types/guid";
import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service";
import { CipherEncryptionService } from "@bitwarden/common/vault/abstractions/cipher-encryption.service";
import { CipherRiskService } from "@bitwarden/common/vault/abstractions/cipher-risk.service";
import { CipherSdkService } from "@bitwarden/common/vault/abstractions/cipher-sdk.service";
import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
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 { DefaultCipherArchiveService } from "@bitwarden/common/vault/services/default-cipher-archive.service";
import { DefaultCipherEncryptionService } from "@bitwarden/common/vault/services/default-cipher-encryption.service";
import { DefaultCipherRiskService } from "@bitwarden/common/vault/services/default-cipher-risk.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 {
  legacyPasswordGenerationServiceFactory,
  PasswordGenerationServiceAbstraction,
} from "@bitwarden/generator-legacy";
import {
  DefaultImportMetadataService,
  ImportApiService,
  ImportApiServiceAbstraction,
  ImportMetadataServiceAbstraction,
  ImportService,
  ImportServiceAbstraction,
} from "@bitwarden/importer-core";
import {
  DefaultKdfConfigService,
  KdfConfigService,
  DefaultKeyService as KeyService,
  BiometricStateService,
  DefaultBiometricStateService,
} from "@bitwarden/key-management";
import { NodeCryptoFunctionService } from "@bitwarden/node/services/node-crypto-function.service";
import {
  ActiveUserStateProvider,
  DerivedStateProvider,
  GlobalStateProvider,
  SingleUserStateProvider,
  StateEventRunnerService,
  StateProvider,
  StateService,
} from "@bitwarden/state";
import {
  DefaultActiveUserStateProvider,
  DefaultDerivedStateProvider,
  DefaultGlobalStateProvider,
  DefaultSingleUserStateProvider,
  DefaultStateEventRegistrarService,
  DefaultStateEventRunnerService,
  DefaultStateProvider,
  DefaultStateService,
} from "@bitwarden/state-internal";
import { SerializedMemoryStorageService } from "@bitwarden/storage-core";
import { DefaultUnlockService, UnlockService } from "@bitwarden/unlock";
import {
  IndividualVaultExportService,
  IndividualVaultExportServiceAbstraction,
  VaultExportApiService,
  OrganizationVaultExportService,
  OrganizationVaultExportServiceAbstraction,
  VaultExportService,
  VaultExportServiceAbstraction,
  DefaultVaultExportApiService,
} from "@bitwarden/vault-export-core";

import { CliBiometricsService } from "../key-management/cli-biometrics-service";
import { CliProcessReloadService } from "../key-management/cli-process-reload.service";
import { CliUserKeyRotationService } from "../key-management/cli-user-key-rotation-service";
import { CliSessionTimeoutTypeService } from "../key-management/session-timeout/services/cli-session-timeout-type.service";
import { flagEnabled } from "../platform/flags";
import { CliPlatformUtilsService } from "../platform/services/cli-platform-utils.service";
import { CliSdkLoadService } from "../platform/services/cli-sdk-load.service";
import { CliSystemService } from "../platform/services/cli-system.service";
import { ConsoleLogService } from "../platform/services/console-log.service";
import { I18nService } from "../platform/services/i18n.service";
import { LowdbStorageService } from "../platform/services/lowdb-storage.service";
import { NodeApiService } from "../platform/services/node-api.service";
import { NodeEnvSecureStorageService } from "../platform/services/node-env-secure-storage.service";
import { CliRestrictedItemTypesService } from "../vault/services/cli-restricted-item-types.service";

// eslint-disable-next-line
const packageJson = require("../../package.json");

/**
 * Instantiates services and makes them available for dependency injection.
 * Any Bitwarden-licensed services should be registered here.
 */
export class ServiceContainer {
  private inited = false;

  messagingService: MessageSender;
  storageService: LowdbStorageService;
  secureStorageService: NodeEnvSecureStorageService;
  memoryStorageService: MemoryStorageService;
  memoryStorageForStateProviders: SerializedMemoryStorageService;
  migrationRunner: MigrationRunner;
  i18nService: I18nService;
  platformUtilsService: CliPlatformUtilsService;
  keyService: KeyService;
  tokenService: TokenService;
  appIdService: AppIdService;
  apiService: NodeApiService;
  twoFactorApiService: TwoFactorApiService;
  hibpApiService: HibpApiService;
  environmentService: EnvironmentService;
  cipherSdkService: CipherSdkService;
  cipherService: CipherService;
  cipherRiskService: CipherRiskService;
  folderService: InternalFolderService;
  organizationUserApiService: OrganizationUserApiService;
  collectionService: DefaultCollectionService;
  vaultTimeoutService: VaultTimeoutService;
  masterPasswordService: InternalMasterPasswordServiceAbstraction;
  vaultTimeoutSettingsService: VaultTimeoutSettingsService;
  syncService: SyncService;
  eventCollectionService: EventCollectionServiceAbstraction;
  eventUploadService: EventUploadServiceAbstraction;
  passwordGenerationService: PasswordGenerationServiceAbstraction;
  passwordStrengthService: PasswordStrengthServiceAbstraction;
  userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction;
  totpService: TotpService;
  containerService: ContainerService;
  auditService: AuditService;
  importService: ImportServiceAbstraction;
  importApiService: ImportApiServiceAbstraction;
  importMetadataService: ImportMetadataServiceAbstraction;
  exportService: VaultExportServiceAbstraction;
  vaultExportApiService: VaultExportApiService;
  individualExportService: IndividualVaultExportServiceAbstraction;
  organizationExportService: OrganizationVaultExportServiceAbstraction;
  searchService: SearchService;
  keyGenerationService: KeyGenerationService;
  cryptoFunctionService: NodeCryptoFunctionService;
  encryptService: EncryptServiceImplementation;
  authService: AuthService;
  policyService: DefaultPolicyService;
  newPolicyService: InternalNewPolicyService;
  policyApiService: PolicyApiServiceAbstraction;
  logService: ConsoleLogService;
  sendService: SendService;
  sendStateProvider: SendStateProvider;
  fileUploadService: FileUploadService;
  cipherFileUploadService: CipherFileUploadService;
  keyConnectorService: KeyConnectorService;
  userVerificationService: UserVerificationService;
  pinService: PinServiceAbstraction;
  stateService: StateService;
  autofillSettingsService: AutofillSettingsServiceAbstraction;
  domainSettingsService: DomainSettingsService;
  organizationService: DefaultOrganizationService;
  DefaultOrganizationService: DefaultOrganizationService;
  providerService: ProviderService;
  twoFactorService: TwoFactorService;
  folderApiService: FolderApiService;
  userVerificationApiService: UserVerificationApiService;
  organizationApiService: OrganizationApiServiceAbstraction;
  sendApiService: SendApiServiceSelector;
  sendTokenService: SendTokenService;
  sendPasswordService: SendPasswordService;
  devicesApiService: DevicesApiServiceAbstraction;
  deviceTrustService: DeviceTrustServiceAbstraction;
  authRequestService: AuthRequestService;
  authRequestApiService: AuthRequestApiServiceAbstraction;
  configApiService: ConfigApiServiceAbstraction;
  configService: ConfigService;
  availableRegionsService: AvailableRegionsService;
  govModeService: GovModeService;
  accountService: AccountService;
  globalStateProvider: GlobalStateProvider;
  singleUserStateProvider: SingleUserStateProvider;
  activeUserStateProvider: ActiveUserStateProvider;
  derivedStateProvider: DerivedStateProvider;
  stateProvider: StateProvider;
  passwordPreloginService: PasswordPreloginService;
  loginStrategyService: LoginStrategyServiceAbstraction;
  avatarService: AvatarServiceAbstraction;
  stateEventRunnerService: StateEventRunnerService;
  biometricStateService: BiometricStateService;
  billingAccountProfileStateService: BillingAccountProfileStateService;
  premiumCheckoutPendingService: PremiumCheckoutPendingService;
  providerApiService: ProviderApiServiceAbstraction;
  userAutoUnlockKeyService: UserAutoUnlockKeyService;
  kdfConfigService: KdfConfigService;
  taskSchedulerService: TaskSchedulerService;
  sdkService: SdkService;
  registerSdkService: RegisterSdkService;
  sdkLoadService: SdkLoadService;
  cipherAuthorizationService: CipherAuthorizationService;
  ssoUrlService: SsoUrlService;
  masterPasswordApiService: MasterPasswordApiServiceAbstraction;
  cipherEncryptionService: CipherEncryptionService;
  collectionEncryptionService: CollectionEncryptionService;
  restrictedItemTypesService: RestrictedItemTypesService;
  cliRestrictedItemTypesService: CliRestrictedItemTypesService;
  encryptedMigrator: EncryptedMigrator;
  securityStateService: SecurityStateService;
  masterPasswordUnlockService: MasterPasswordUnlockService;
  cipherArchiveService: CipherArchiveService;
  lockService: LockService;
  unlockService: UnlockService;
  private accountCryptographicStateService: DefaultAccountCryptographicStateService;
  private v2UpgradeTokenStateService: V2UpgradeTokenStateService;

  constructor() {
    let p = null;
    const relativeDataDir = path.join(path.dirname(process.execPath), "bw-data");
    if (fs.existsSync(relativeDataDir)) {
      p = relativeDataDir;
    } else if (process.env.BITWARDENCLI_APPDATA_DIR) {
      p = path.resolve(process.env.BITWARDENCLI_APPDATA_DIR);
    } else if (process.platform === "darwin") {
      p = path.join(process.env.HOME ?? "", "Library/Application Support/Bitwarden CLI");
    } else if (process.platform === "win32") {
      p = path.join(process.env.APPDATA ?? "", "Bitwarden CLI");
    } else if (process.env.XDG_CONFIG_HOME) {
      p = path.join(process.env.XDG_CONFIG_HOME, "Bitwarden CLI");
    } else {
      p = path.join(process.env.HOME ?? "", ".config/Bitwarden CLI");
    }

    const logoutCallback = async () => await this.logout();

    this.platformUtilsService = new CliPlatformUtilsService(ClientType.Cli, packageJson);
    this.logService = new ConsoleLogService(
      this.platformUtilsService.isDev(),
      (level) => process.env.BITWARDENCLI_DEBUG !== "true" && level <= LogLevelType.Info,
    );
    this.cryptoFunctionService = new NodeCryptoFunctionService();
    this.encryptService = new EncryptServiceImplementation(
      this.cryptoFunctionService,
      this.logService,
      true,
    );
    this.storageService = new LowdbStorageService(this.logService, null, p, false, true);
    this.secureStorageService = new NodeEnvSecureStorageService(
      this.storageService,
      this.logService,
      // MAC failures for secure storage are being logged for customers today and
      // they occur when users unlock / login and refresh a session key but don't
      // export it into their environment (e.g. BW_SESSION_KEY). This leaves a stale
      // BW_SESSION key in the env which is attempted to be used to decrypt the auto
      // unlock user key which obviously fails. So, to resolve this, we will not log
      // MAC failures for secure storage.
      new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false),
    );

    this.memoryStorageService = new MemoryStorageService();
    this.memoryStorageForStateProviders = new SerializedMemoryStorageService();

    const storageServiceProvider = new StorageServiceProvider(
      this.storageService,
      this.memoryStorageForStateProviders,
    );

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

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

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

    this.i18nService = new I18nService("en", "./locales", this.globalStateProvider);

    this.singleUserStateProvider = new DefaultSingleUserStateProvider(
      storageServiceProvider,
      stateEventRegistrarService,
      this.logService,
    );

    this.messagingService = MessageSender.EMPTY;

    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 DefaultDerivedStateProvider();

    this.stateProvider = new DefaultStateProvider(
      this.activeUserStateProvider,
      this.singleUserStateProvider,
      this.globalStateProvider,
      this.derivedStateProvider,
    );

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

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

    this.securityStateService = new DefaultSecurityStateService(
      this.accountCryptographicStateService,
    );

    this.environmentService = new DefaultEnvironmentService(
      this.stateProvider,
      this.accountService,
      process.env.ADDITIONAL_REGIONS as unknown as RegionConfig[],
    );

    this.keyGenerationService = new DefaultKeyGenerationService(this.cryptoFunctionService);

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

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

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

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

    this.keyService = new KeyService(
      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);

    const customUserAgent =
      "Bitwarden_CLI/" +
      this.platformUtilsService.getApplicationVersionSync() +
      " (" +
      this.platformUtilsService.getDeviceString().toUpperCase() +
      ")";

    this.biometricStateService = new DefaultBiometricStateService(this.stateProvider);
    this.userDecryptionOptionsService = new UserDecryptionOptionsService(
      this.singleUserStateProvider,
    );
    this.ssoUrlService = new SsoUrlService();

    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 CliSessionTimeoutTypeService();

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

    const refreshAccessTokenErrorCallback = () => {
      throw new Error("Refresh Access token error");
    };

    this.apiService = new NodeApiService(
      this.tokenService,
      this.platformUtilsService,
      this.environmentService,
      this.appIdService,
      refreshAccessTokenErrorCallback,
      this.logService,
      logoutCallback,
      this.vaultTimeoutSettingsService,
      this.accountService,
      customUserAgent,
    );

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

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

    this.twoFactorApiService = new DefaultTwoFactorApiService(this.apiService);

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

    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.domainSettingsService = new DefaultDomainSettingsService(
      this.stateProvider,
      this.policyService,
      this.accountService,
      this.configService,
      this.environmentService,
      this.authService,
    );

    this.fileUploadService = new FileUploadService(
      this.logService,
      this.apiService,
      this.configService,
    );

    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,
    );
    const sdkClientFactory = flagEnabled("sdk")
      ? new DefaultSdkClientFactory()
      : new NoopSdkClientFactory();
    this.sdkLoadService = new CliSdkLoadService();
    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,
      customUserAgent,
    );

    this.sendApiService = new SendApiServiceSelector(
      this.configService,
      legacySendApiService,
      new SendSdkApiService(
        this.sdkService,
        legacySendApiService,
        this.sendService,
        this.accountService,
        this.logService,
      ),
    );

    this.sendPasswordService = new DefaultSendPasswordService(this.cryptoFunctionService);

    this.searchService = new SearchService(this.logService, this.i18nService);

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

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

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

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

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

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

    this.unlockService = new DefaultUnlockService(
      this.registerSdkService,
      this.accountCryptographicStateService,
      this.kdfConfigService,
      this.accountService,
      this.masterPasswordService,
      this.stateProvider,
      this.logService,
      new CliBiometricsService(),
      this.platformUtilsService,
      this.stateService,
      this.biometricStateService,
      this.v2UpgradeTokenStateService,
    );

    this.sendTokenService = new DefaultSendTokenService(
      this.globalStateProvider,
      this.sdkService,
      this.sendPasswordService,
    );

    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.twoFactorService = new DefaultTwoFactorService(
      this.i18nService,
      this.platformUtilsService,
      this.globalStateProvider,
      this.twoFactorApiService,
    );

    this.passwordStrengthService = new PasswordStrengthService();

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

    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,
    );

    this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService(
      this.stateProvider,
    );
    this.premiumCheckoutPendingService = new DefaultPremiumCheckoutPendingService(
      this.stateProvider,
    );

    this.taskSchedulerService = new DefaultTaskSchedulerService(this.logService);

    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,
    );

    const passwordPreloginApiService = new PasswordPreloginApiService(
      this.apiService,
      this.environmentService,
    );
    this.passwordPreloginService = new DefaultPasswordPreloginService(
      passwordPreloginApiService,
      this.sdkService,
      this.environmentService,
      this.configService,
    );

    const loginStrategyCacheService = new DefaultLoginStrategyCacheService(
      this.globalStateProvider,
    );

    const loginStrategySessionTimeoutService = new DefaultLoginStrategySessionTimeoutService(
      this.taskSchedulerService,
      loginStrategyCacheService,
      this.logService,
      this.messagingService,
      MessageListener.EMPTY,
    );
    this.loginStrategyService = new LoginStrategyService(
      this.accountService,
      this.masterPasswordService,
      this.keyService,
      this.apiService,
      this.tokenService,
      this.appIdService,
      this.platformUtilsService,
      this.messagingService,
      this.logService,
      this.keyConnectorService,
      this.environmentService,
      this.stateService,
      this.twoFactorService,
      this.i18nService,
      this.encryptService,
      this.passwordStrengthService,
      this.policyService,
      this.deviceTrustService,
      this.authRequestService,
      this.userDecryptionOptionsService,
      this.globalStateProvider,
      this.billingAccountProfileStateService,
      this.vaultTimeoutSettingsService,
      this.kdfConfigService,
      this.configService,
      this.accountCryptographicStateService,
      this.passwordPreloginService,
      this.unlockService,
      loginStrategyCacheService,
      loginStrategySessionTimeoutService,
    );

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

    this.cliRestrictedItemTypesService = new CliRestrictedItemTypesService(
      this.restrictedItemTypesService,
    );

    // FIXME: CLI does not support autofill
    this.autofillSettingsService = new AutofillSettingsService(
      this.stateProvider,
      this.policyService,
      this.accountService,
      this.restrictedItemTypesService,
    );

    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.cipherArchiveService = new DefaultCipherArchiveService(
      this.cipherService,
      this.apiService,
      this.billingAccountProfileStateService,
    );

    this.cipherRiskService = new DefaultCipherRiskService(this.sdkService, this.cipherService);

    this.folderService = new FolderService(
      this.keyService,
      this.encryptService,
      this.i18nService,
      this.cipherService,
      this.stateProvider,
    );

    this.folderApiService = new FolderApiService(this.folderService, this.apiService);

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

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

    const biometricService = new CliBiometricsService();
    const logoutService = new DefaultLogoutService(this.messagingService);
    const processReloadService = new CliProcessReloadService();
    const systemService = new CliSystemService();
    this.lockService = new DefaultLockService(
      this.accountService,
      biometricService,
      this.vaultTimeoutSettingsService,
      logoutService,
      this.messagingService,
      this.searchService,
      this.folderService,
      this.masterPasswordService,
      this.stateEventRunnerService,
      this.cipherService,
      this.authService,
      systemService,
      processReloadService,
      this.logService,
      this.keyService,
    );

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

    this.avatarService = new AvatarService(this.apiService, 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.totpService = new TotpService(this.sdkService);

    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.individualExportService = new IndividualVaultExportService(
      this.folderService,
      this.cipherService,
      this.keyGenerationService,
      this.keyService,
      this.encryptService,
      this.kdfConfigService,
      this.apiService,
      this.restrictedItemTypesService,
      this.logService,
    );

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

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

    this.exportService = new VaultExportService(
      this.individualExportService,
      this.organizationExportService,
      this.accountService,
    );

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

    this.hibpApiService = new HibpApiService(this.apiService);
    this.auditService = new AuditService(
      this.cryptoFunctionService,
      this.apiService,
      this.hibpApiService,
    );

    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.organizationApiService = new OrganizationApiService(this.apiService, this.syncService);

    this.providerApiService = new ProviderApiService(this.apiService);

    this.organizationUserApiService = new DefaultOrganizationUserApiService(this.apiService);

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

    this.masterPasswordApiService = new MasterPasswordApiService(this.apiService, this.logService);
    this.encryptedMigrator = new DefaultEncryptedMigrator(
      this.kdfConfigService,
      this.logService,
      this.configService,
      this.masterPasswordService,
      this.syncService,
      this.keyService,
      new CliBiometricsService(),
      this.biometricStateService,
      this.platformUtilsService,
      new CliUserKeyRotationService(),
      this.cipherService,
      this.sdkService,
    );
  }

  async logout() {
    this.authService.logOut(() => {
      /* Do nothing */
    });
    const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
    await Promise.all([
      this.eventUploadService.uploadEvents(userId as UserId),
      this.keyService.clearKeys(userId),
      this.cipherService.clear(userId),
      this.folderService.clear(userId),
    ]);

    await this.stateEventRunnerService.handleEvent("logout", userId as UserId);

    await this.stateService.clean({ userId: userId });
    await this.tokenService.clearTokens(userId);
    await this.accountService.clean(userId as UserId);
    await this.accountService.switchAccount(null);
    process.env.BW_SESSION = undefined;
  }

  async init() {
    if (this.inited) {
      this.logService.warning("ServiceContainer.init called more than once");
      return;
    }

    await this.sdkLoadService.loadAndInit();
    await this.storageService.init();

    await this.migrationRunner.run();
    this.containerService.attachToGlobal(global);
    await this.i18nService.init();
    this.twoFactorService.init();

    const accounts = await firstValueFrom(this.accountService.accounts$);
    await this.tokenService.cleanupTokenStorage(Object.keys(accounts) as UserId[]);

    // If a user has a BW_SESSION key stored in their env (not process.env.BW_SESSION),
    // this should set the user key to unlock the vault on init.
    // TODO: ideally, we wouldn't want to do this here but instead only for commands that require the vault to be unlocked
    // as this runs on every command and could be a performance hit
    const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
    if (activeAccount?.id) {
      await this.userAutoUnlockKeyService.setUserKeyInMemoryIfAutoUserKeySet(activeAccount.id);
    }

    this.inited = true;
  }
}
