// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_SYNC_SERVICE_SYNC_PREFS_H_
#define COMPONENTS_SYNC_SERVICE_SYNC_PREFS_H_

#include <optional>
#include <string>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/sequence_checker.h"
#include "build/build_config.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_member.h"
#include "components/signin/public/base/signin_buildflags.h"
#include "components/sync/base/passphrase_enums.h"
#include "components/sync/base/user_selectable_type.h"

class GaiaId;
class PrefRegistrySimple;
class PrefService;
class PrefValueMap;

namespace os_crypt_async {
class Encryptor;
}  // namespace os_crypt_async

namespace sync_pb {
class TrustedVaultAutoUpgradeExperimentGroup;
}  // namespace sync_pb

namespace syncer {

class CustomPassphraseBootstrapToken;

class SyncPrefObserver : public base::CheckedObserver {
 public:
  virtual void OnSyncManagedPrefChange(bool is_sync_managed) = 0;
  // Called when any of the prefs related to the user's selected data types has
  // changed.
  virtual void OnSelectedTypesPrefChange() = 0;

 protected:
  ~SyncPrefObserver() override;
};

// SyncPrefs is a helper class that manages getting, setting, and persisting
// global sync preferences. It is not thread-safe, and lives on the UI thread.
class SyncPrefs {
 public:
  enum class SyncAccountState {
    kNotSignedIn,
    kSignedInWithoutSyncConsent,
    // Note that kSyncing is also used for local sync (an enterprise feature
    // known as roaming profiles) as well as in some advanced transport-mode
    // scenarios such as the advanced sync setup flow interrupted case or, on
    // ChromeOS, after sync is reset from the sync dashboard.
    kSyncing,
  };

  // `pref_service` must not be null and must outlive this object.
  explicit SyncPrefs(PrefService* pref_service);

  SyncPrefs(const SyncPrefs&) = delete;
  SyncPrefs& operator=(const SyncPrefs&) = delete;

  ~SyncPrefs();

  static void RegisterProfilePrefs(PrefRegistrySimple* registry);

  void AddObserver(SyncPrefObserver* sync_pref_observer);
  void RemoveObserver(SyncPrefObserver* sync_pref_observer);

  // Getters and setters for global sync prefs.

  // First-Setup-Complete is conceptually similar to the user's consent to
  // enable sync-the-feature.
  bool IsInitialSyncFeatureSetupComplete() const;

  // ChromeOS Ash, IsInitialSyncFeatureSetupComplete() always returns true.
#if !BUILDFLAG(IS_CHROMEOS)
  void SetInitialSyncFeatureSetupComplete();
  void ClearInitialSyncFeatureSetupComplete();
#endif  // !BUILDFLAG(IS_CHROMEOS)

  // Whether the "Sync everything" toggle is enabled. This flag only has an
  // effect if Sync-the-feature is enabled. Note that even if this is true, some
  // types may be disabled e.g. due to enterprise policy.
  bool HasKeepEverythingSynced() const;

  // Returns the set of types that the user has selected to be synced.
  // This is only used for syncing users and takes HasKeepEverythingSynced()
  // into account (i.e. returns "all types").
  // If some types are force-disabled by policy, they will not be included.
  UserSelectableTypeSet GetSelectedTypesForSyncingUser() const;
  // Returns the set of types for the given gaia_id for signed-in users.
  // If some types are force-disabled by policy, they will not be included.
  // Note: this is used for signed-in not syncing users.
  UserSelectableTypeSet GetSelectedTypesForAccount(const GaiaId& gaia_id) const;

  // Returns whether `type` is "managed" i.e. controlled by enterprise policy.
  bool IsTypeManagedByPolicy(UserSelectableType type) const;

  // Returns whether `type` is "managed" i.e. controlled by a custodian (i.e.
  // parent/guardian of a child account).
  bool IsTypeManagedByCustodian(UserSelectableType type) const;

  // Returns true if no value exists for the type pref. Otherwise,
  // returns false.
  bool DoesTypeHaveDefaultValueForAccount(const UserSelectableType type,
                                          const GaiaId& gaia_id);

  // Returns true if the type is disabled; that was either set by a user
  // choice, or when a policy enforces disabling the type. Otherwise, returns
  // false if no value exists for the type pref (default), or if it is enabled.
  // Note: this method checks the actual pref value even if there is a policy
  // applied on the type.
  bool IsTypeDisabledByUserForAccount(const UserSelectableType type,
                                      const GaiaId& gaia_id);

  // Sets the selection state for all `registered_types` and "keep everything
  // synced" flag.
  // `keep_everything_synced` indicates that all current and future types
  // should be synced. If this is set to true, then GetSelectedTypes() will
  // always return UserSelectableTypeSet::All(), even if not all of them are
  // registered or individually marked as selected.
  // Changes are still made to the individual selectable type prefs even if
  // `keep_everything_synced` is true, but won't be visible until it's set to
  // false.
  void SetSelectedTypesForSyncingUser(bool keep_everything_synced,
                                      UserSelectableTypeSet registered_types,
                                      UserSelectableTypeSet selected_types);
  // Used to set user's selected types prefs in Sync-the-transport mode.
  // Note: this is used for signed-in not syncing users.
  void SetSelectedTypeForAccount(UserSelectableType type,
                                 bool is_type_on,
                                 const GaiaId& gaia_id);
  // Used to reset user's selected types prefs in Sync-the-transport mode to its
  // default value. Note: this is used for signed-in not syncing users.
  void ResetSelectedTypeForAccount(UserSelectableType type,
                                   const GaiaId& gaia_id);

  // Used to clear per account prefs for all users *except* the ones in the
  // passed-in `available_gaia_ids`.
  void KeepAccountSettingsPrefsOnlyForUsers(
      const std::vector<GaiaId>& available_gaia_ids);

#if BUILDFLAG(IS_CHROMEOS)
  // Functions to deal with the Ash-specific state where sync-the-feature is
  // disabled because the user reset sync via dashboard.
  bool IsSyncFeatureDisabledViaDashboard() const;
  void SetSyncFeatureDisabledViaDashboard();
  void ClearSyncFeatureDisabledViaDashboard();

  // Chrome OS provides a separate settings UI surface for sync of OS types,
  // including a separate "Sync All" toggle for OS types.
  bool IsSyncAllOsTypesEnabled() const;
  UserSelectableOsTypeSet GetSelectedOsTypes() const;
  bool IsOsTypeManagedByPolicy(UserSelectableOsType type) const;
  void SetSelectedOsTypes(bool sync_all_os_types,
                          UserSelectableOsTypeSet registered_types,
                          UserSelectableOsTypeSet selected_types);

  // Maps `type` to its corresponding preference name.
  static const char* GetPrefNameForOsTypeForTesting(UserSelectableOsType type);

  // Sets `type` as disabled in the given `policy_prefs`, which should
  // correspond to the "managed" (aka policy-controlled) pref store.
  static void SetOsTypeDisabledByPolicy(PrefValueMap* policy_prefs,
                                        UserSelectableOsType type);
#endif  // BUILDFLAG(IS_CHROMEOS)

  // Whether Sync is disabled on the client for all profiles and accounts.
  bool IsSyncClientDisabledByPolicy() const;

  // Maps `type` to its corresponding preference name.
  static const char* GetPrefNameForTypeForTesting(UserSelectableType type);

  // Sets `type` as disabled in the given `policy_prefs`, which should
  // correspond to the "managed" (aka policy-controlled) pref store.
  static void SetTypeDisabledByPolicy(PrefValueMap* policy_prefs,
                                      UserSelectableType type);

  // Sets `type` as disabled in the given `supervised_user_prefs`, which should
  // correspond to the custodian-controlled pref store (i.e. controlled by
  // parent/guardian of a child account).
  static void SetTypeDisabledByCustodian(PrefValueMap* supervised_user_prefs,
                                         UserSelectableType type);

  // Gets the local sync backend enabled state.
  bool IsLocalSyncEnabled() const;

  // The user's passphrase type, determined the first time the engine is
  // successfully initialized.
  std::optional<PassphraseType> GetCachedPassphraseType() const;
  void SetCachedPassphraseType(PassphraseType passphrase_type);
  void ClearCachedPassphraseType();

  // Cached notion of whether or not a persistent auth error exists, useful
  // during profile startup before IdentityManager can determine the
  // authoritative value.
  bool HasCachedPersistentAuthErrorForMetrics() const;
  void SetHasCachedPersistentAuthErrorForMetrics(
      bool has_persistent_auth_error);
  void ClearCachedPersistentAuthErrorForMetrics();

  // The user's TrustedVaultAutoUpgradeExperimentGroup, determined the first
  // time the engine is successfully initialized.
  std::optional<sync_pb::TrustedVaultAutoUpgradeExperimentGroup>
  GetCachedTrustedVaultAutoUpgradeExperimentGroup() const;
  void SetCachedTrustedVaultAutoUpgradeExperimentGroup(
      const sync_pb::TrustedVaultAutoUpgradeExperimentGroup& group);
  void ClearCachedTrustedVaultAutoUpgradeExperimentGroup();

  // The encryption bootstrap token is used for explicit passphrase users
  // (usually custom passphrase) and represents a user-entered passphrase.
  // TODO(crbug.com/369297671): ClearAllEncryptionBootstrapTokens is only needed
  // to clear the gaia-keyed pref on signout for syncing users. It should be
  // removed only when kMigrateSyncingUserToSignedIn is fully rolled-out.
  void ClearAllEncryptionBootstrapTokens();
  // The encryption bootstrap token per account. Used for explicit passphrase
  // users (usually custom passphrase) and represents a user-entered passphrase.
  CustomPassphraseBootstrapToken GetEncryptionBootstrapTokenForAccount(
      const os_crypt_async::Encryptor& encryptor,
      const GaiaId& gaia_id) const;
  void SetEncryptionBootstrapTokenForAccount(
      const CustomPassphraseBootstrapToken& token,
      const os_crypt_async::Encryptor& encryptor,
      const GaiaId& gaia_id);
  void ClearEncryptionBootstrapTokenForAccount(const GaiaId& gaia_id);

  // Muting mechanism for passphrase prompts, used on Android.
  int GetPassphrasePromptMutedProductVersion() const;
  void SetPassphrasePromptMutedProductVersion(int major_version);
  void ClearPassphrasePromptMutedProductVersion();

  // Migrates any user settings for pre-existing signed-in users, for the
  // feature `kReplaceSyncPromosWithSignInPromos`. For signed-out users or
  // syncing users, no migration is necessary - this also covers new users (or
  // more precisely, new profiles).
  // This should be called early during browser startup.
  // Returns whether the migration ran, i.e. whether any user settings were set.
  bool MaybeMigratePrefsForSyncToSigninPart1(SyncAccountState account_state,
                                             const GaiaId& gaia_id);

  // Second part of the above migration, which depends on the user's passphrase
  // type, which isn't known yet during browser startup. This should be called
  // as soon as the passphrase type is known, and will only do any migration if
  // the above method has flagged that it's necessary.
  // Returns whether the migration ran, i.e. whether any user settings were set.
  bool MaybeMigratePrefsForSyncToSigninPart2(const GaiaId& gaia_id,
                                             bool is_using_explicit_passphrase);

  // Migrates kSyncEncryptionBootstrapToken to the gaia-keyed pref, for the
  // feature `kSyncRememberCustomPassphraseAfterSignout`. This should be called
  // early during browser startup.
  // TODO(crbug.com/325201878): Clean up the migration logic and the old pref.
  void MaybeMigrateCustomPassphrasePref(const GaiaId& gaia_id);

  // Should be called when Sync gets disabled / the user signs out. Clears any
  // temporary state from the above migration.
  void MarkPartialSyncToSigninMigrationFullyDone();

  // Copies the global versions of the selected-types prefs (used for syncing
  // users) to the per-account prefs for the given `gaia_id` (used for signed-in
  // non-syncing users). To be used when an existing syncing user is migrated to
  // signed-in.
  static void MigrateGlobalDataTypePrefsToAccount(PrefService* pref_service,
                                                  const GaiaId& gaia_id);

  // Returns whether a UserSelectableType is enabled by default in transport
  // mode, that is, without an explicit value stored in prefs.
  bool IsTypeSelectedByDefaultInTransportMode(UserSelectableType type,
                                              const GaiaId& gaia_id) const;

 private:
  static void RegisterTypeSelectedPref(PrefRegistrySimple* prefs,
                                       UserSelectableType type);

  static const char* GetPrefNameForType(UserSelectableType type);
#if BUILDFLAG(IS_CHROMEOS)
  static const char* GetPrefNameForOsType(UserSelectableOsType type);
#endif  // BUILDFLAG(IS_CHROMEOS)

  static bool IsTypeSupportedInTransportMode(UserSelectableType type);

  void OnSyncManagedPrefChanged();

  void OnSelectedTypesPrefChanged(const std::string& pref_name);

  // Never null.
  const raw_ptr<PrefService> pref_service_;

  // TODO(crbug.com/484371187): Investigate if reentrancy can be removed.
  base::ObserverList<
      SyncPrefObserver,
      /*check_empty=*/false,
      base::ObserverListReentrancyPolicy::kAllowReentrancyUntriaged>
      sync_pref_observers_;

  // The preference that controls whether sync is under control by
  // configuration management (aka policy).
  BooleanPrefMember pref_sync_managed_;

  PrefChangeRegistrar pref_change_registrar_;

  bool batch_updating_selected_types_ = false;

  // Caches the value of the kEnableLocalSyncBackend pref to avoid it flipping
  // during the lifetime of the service.
  const bool local_sync_enabled_;

  SEQUENCE_CHECKER(sequence_checker_);
};

}  // namespace syncer

#endif  // COMPONENTS_SYNC_SERVICE_SYNC_PREFS_H_
