// Copyright 2021 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_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCOUNT_CAPABILITIES_H_
#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCOUNT_CAPABILITIES_H_

#include <optional>
#include <string>
#include <string_view>

#include "base/containers/flat_map.h"
#include "base/containers/span.h"
#include "base/gtest_prod_util.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/signin/internal/identity_manager/account_info_util.h"
#include "components/signin/public/identity_manager/tribool.h"

#if BUILDFLAG(IS_ANDROID)
#include "base/android/scoped_java_ref.h"
#endif

namespace ios {
class AccountCapabilitiesFetcherIOS;
}  // namespace ios

namespace supervised_user {
class FamilyLinkUserCapabilitiesObserver;
}  // namespace supervised_user

// Stores the information about account capabilities. Capabilities provide
// information about state and features of Gaia accounts.
class AccountCapabilities {
 public:
  AccountCapabilities();
  ~AccountCapabilities();
  AccountCapabilities(const AccountCapabilities& other);
  AccountCapabilities(AccountCapabilities&& other) noexcept;
  AccountCapabilities& operator=(const AccountCapabilities& other);
  AccountCapabilities& operator=(AccountCapabilities&& other) noexcept;

#if BUILDFLAG(IS_ANDROID)
  static AccountCapabilities ConvertFromJavaAccountCapabilities(
      JNIEnv* env,
      const base::android::JavaRef<jobject>& accountCapabilities);

  base::android::ScopedJavaLocalRef<jobject> ConvertToJavaAccountCapabilities(
      JNIEnv* env) const;
#endif

  explicit AccountCapabilities(base::flat_map<std::string, bool> capabilities);

#if BUILDFLAG(IS_IOS)
  const base::flat_map<std::string, bool>& ConvertToAccountCapabilitiesIOS();
#endif

  // Resets the cached list of supported account capability names.
  // Useful for testing scenarios where feature flags change between tests.
  static void ResetSupportedAccountCapabilityNamesForTesting();

  // clang-format off
  // keep-sorted start newline_separated=yes sticky_prefixes=#if,BUILDFLAG group_prefixes=#endif,can,has,is,must
  // clang-format on
  // Chrome can fetch information related to the family
  // group for accounts with this capability.
  signin::Tribool can_fetch_family_member_info() const;

#if !BUILDFLAG(IS_IOS)
  // Chrome can display the email address for accounts with this capability.
  signin::Tribool can_have_email_address_displayed() const;
#endif

#if !BUILDFLAG(IS_ANDROID)
  // The primary account type is suitable for choice screens. Signals that are
  // not account-type specific should be checked separately.
  signin::Tribool can_make_chrome_search_engine_choice_screen_choice() const;
#endif

  // Chrome can override the account info for accounts with this capability.
  signin::Tribool can_override_account_info() const;

#if !BUILDFLAG(IS_IOS)
  // Chrome can run privacy sandbox trials for accounts with this capability.
  signin::Tribool can_run_chrome_privacy_sandbox_trials() const;
#endif

  // Chrome can show history sync opt in screens without minor mode
  // restrictions with this capability.
  signin::Tribool
  can_show_history_sync_opt_ins_without_minor_mode_restrictions() const;

#if BUILDFLAG(IS_IOS)
  // Whether the user is allowed to sign in to Chrome.
  signin::Tribool can_sign_in_to_chrome() const;
#endif

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
    BUILDFLAG(IS_IOS)
  // Whether the account can submit feedback. For iOS, this is implemented by
  // Aloha FeedbackKit. For Android, this is implemented by GMS Core.
  signin::Tribool can_submit_feedback() const;
#endif

#if BUILDFLAG(IS_CHROMEOS)
  // Chrome can toggle auto updates with this capability.
  signin::Tribool can_toggle_auto_updates() const;
#endif

#if BUILDFLAG(IS_CHROMEOS)
  // The user account is able to use generative AI features. Since many
  // generative AI features inherit the same capability (minor restrictions),
  // this one should be used for future generative AI features, instead of using
  // a separate one for each of them.
  signin::Tribool can_use_chromeos_generative_ai() const;
#endif  // BUILDFLAG(IS_CHROMEOS)

#if !BUILDFLAG(IS_IOS)
  // The user account is able to use DevTools AI features.
  signin::Tribool can_use_devtools_generative_ai_features() const;
#endif

#if !BUILDFLAG(IS_IOS)
  // The user account is able to use edu features.
  signin::Tribool can_use_edu_features() const;
#endif

  // The user account is able to use Gemini in Chrome.
  signin::Tribool can_use_gemini_in_chrome() const;

#if BUILDFLAG(IS_CHROMEOS)
  // The user account is able to use generative AI in recorder app.
  signin::Tribool can_use_generative_ai_in_recorder_app() const;
#endif

#if BUILDFLAG(IS_CHROMEOS)
  // The user account is able to use generative AI photo editing.
  signin::Tribool can_use_generative_ai_photo_editing() const;
#endif

  // The user account is able to use manta service.
  signin::Tribool can_use_manta_service() const;

  // The user account is able to use model execution features.
  signin::Tribool can_use_model_execution_features() const;

  // The user account is able to use speaker label in recorder app.
  signin::Tribool can_use_speaker_label_in_recorder_app() const;

  // Chrome can send user data to Google servers for machine learning purposes
  // with this capability.
  signin::Tribool is_allowed_for_machine_learning() const;

  // The user account has opted in to parental supervision (Geller account).
  // Chrome applies parental controls to accounts with this capability.
  signin::Tribool is_opted_in_to_parental_supervision() const;

  // Chrome applies account level enterprise policies to profiles signed in
  // with accounts with this capability.
  signin::Tribool is_subject_to_account_level_enterprise_policies() const;

  // Chrome must show the notice before using the privacy sandbox restricted
  // measurement API
  signin::Tribool
  is_subject_to_chrome_privacy_sandbox_restricted_measurement_notice() const;

  // Chrome applies enterprise features to accounts with this capability.
  // This capability returns true all managed accounts and does not reflect
  // whether the account will receive account level enterprise policies or not
  // when signed in to Chrome.
  signin::Tribool is_subject_to_enterprise_features() const;

  // Chrome applies parental controls to accounts with this capability.
  signin::Tribool is_subject_to_parental_controls() const;

  // Whether the user is subject to Universal Opt-Out.
  signin::Tribool is_subject_to_universal_opt_out() const;

#if BUILDFLAG(IS_IOS)
  // Whether the client must fetch Apple's age range in Chrome.
  signin::Tribool must_fetch_apple_age_range_in_chrome() const;
#endif

#if BUILDFLAG(IS_IOS)
  // Whether the client must skip Apple's age range check in Chrome.
  signin::Tribool must_skip_apple_age_range_in_chrome() const;
#endif

  // Whether the account may fill and save Wallet private passes in Autofill.
  signin::Tribool supports_wallet_private_passes_in_autofill() const;

  // keep-sorted end

  // Whether at least one of the capabilities is not
  // `signin::Tribool::kUnknown`.
  bool AreAnyCapabilitiesKnown() const;

  // Whether none of the capabilities has `signin::Tribool::kUnknown`.
  bool AreAllCapabilitiesKnown() const;

  // Updates the capability state value for keys in `other`. If a value is
  // `signin::Tribool::kUnknown` in `other` the corresponding key will not
  // be updated in order to avoid overriding known values.
  bool UpdateWith(const AccountCapabilities& other);

  bool operator==(const AccountCapabilities& other) const;

 private:
  // Methods to override a specific capability. Passing std::nullopt clears the
  // override.
  void SetCapabilityOverride(std::string_view name,
                             std::optional<signin::Tribool> value);

  // Returns the list of account capability service names supported in Chrome.
  static base::span<const std::string_view>
  GetSupportedAccountCapabilityNames();

  // Returns a friendly display name for the given capability API name.
  static std::string GetCapabilityDisplayName(std::string_view name);

  // Returns the effective capability state using the service name.
  signin::Tribool GetCapabilityByName(std::string_view name) const;

  // Returns the capability state using the service name, without checking
  // overrides.
  signin::Tribool GetFetchedCapabilityByName(std::string_view name) const;

  const base::flat_map<std::string, signin::Tribool>& GetCapabilityOverrides()
      const;

  // Internal version of GetSupportedAccountCapabilityNames that calculates the
  // list on each call, rather than returning a cached value.
  static std::vector<std::string_view>
  GetSupportedAccountCapabilityNamesInternal();

  friend std::optional<AccountCapabilities>
  signin::AccountCapabilitiesFromServerResponse(
      const base::DictValue& account_capabilities);
  friend base::DictValue signin::SerializeAccountCapabilities(
      const AccountCapabilities& account_capabilities);
  friend base::DictValue signin::SerializeAccountCapabilityOverrides(
      const AccountCapabilities& account_capabilities);
  friend AccountCapabilities signin::DeserializeAccountCapabilities(
      const base::DictValue& capabilities_dict,
      const base::DictValue& overrides_dict);
  friend class AboutSigninInternals;
  friend class AccountCapabilitiesFetcherGaia;
#if BUILDFLAG(IS_IOS)
  friend base::span<const std::string_view>
  GetAccountCapabilityNamesForPrefetch();
  friend class ios::AccountCapabilitiesFetcherIOS;
#endif
  FRIEND_TEST_ALL_PREFIXES(AccountCapabilitiesTest,
                           GetSupportedAccountCapabilityNames);
  FRIEND_TEST_ALL_PREFIXES(AccountCapabilitiesTest, CapabilityOverrides);
  FRIEND_TEST_ALL_PREFIXES(AccountCapabilitiesTest,
                           GetSupportedAccountCapabilityNames_FlagDisabled);
  FRIEND_TEST_ALL_PREFIXES(AccountCapabilitiesTest,
                           GetSupportedAccountCapabilityNames_FlagEnabled);
  FRIEND_TEST_ALL_PREFIXES(AccountCapabilitiesTest,
                           ConversionWithJNI_FlagGuardDisabled_JavaToCpp);
  FRIEND_TEST_ALL_PREFIXES(AccountCapabilitiesTest,
                           ConversionWithJNI_FlagGuardDisabled_CppToJava);
  friend class AccountCapabilitiesTestMutator;
  friend class AccountTrackerService;
  friend class supervised_user::FamilyLinkUserCapabilitiesObserver;

  base::flat_map<std::string, bool> capabilities_map_;
  base::flat_map<std::string, signin::Tribool> capabilities_overrides_;
};

#endif  // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCOUNT_CAPABILITIES_H_
