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

#include "chrome/browser/ui/webauthn/sheet_models.h"

#include <algorithm>
#include <cstddef>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <variant>
#include <vector>

#include "base/check.h"
#include "base/check_op.h"
#include "base/feature_list.h"
#include "base/memory/ptr_util.h"
#include "base/notreached.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/ui/webauthn/user_actions.h"
#include "chrome/browser/ui/webauthn/webauthn_ui_helpers.h"
#include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
#include "chrome/browser/webauthn/local_authentication_token.h"
#include "chrome/browser/webauthn/webauthn_metrics_util.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/grit/generated_resources.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/strings/grit/components_strings.h"
#include "device/fido/pin.h"
#include "device/fido/public/features.h"
#include "device/fido/public/fido_constants.h"
#include "device/fido/public/fido_types.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/style/typography.h"

#if BUILDFLAG(IS_MAC)
#include "device/fido/mac/util.h"
#endif

namespace {

using CredentialMech = AuthenticatorRequestDialogModel::Mechanism::Credential;
using EnclaveMech = AuthenticatorRequestDialogModel::Mechanism::Enclave;
using PasswordMech = AuthenticatorRequestDialogModel::Mechanism::Password;
using ICloudKeychainMech =
    AuthenticatorRequestDialogModel::Mechanism::ICloudKeychain;
using Step = AuthenticatorRequestDialogModel::Step;

constexpr int kGpmArbitraryPinMinLength = 4;

bool IsLocalPasskeyOrEnclaveAuthenticatorOrPassword(
    const AuthenticatorRequestDialogModel::Mechanism& mech) {
  return (std::holds_alternative<CredentialMech>(mech.type) &&
          std::get<CredentialMech>(mech.type).value().source !=
              device::AuthenticatorType::kPhone) ||
         std::holds_alternative<EnclaveMech>(mech.type) ||
         std::holds_alternative<PasswordMech>(mech.type);
}

// Possibly returns a resident key warning if the model indicates that it's
// needed.
std::u16string PossibleResidentKeyWarning(
    AuthenticatorRequestDialogModel* dialog_model) {
  switch (dialog_model->resident_key_requirement) {
    case device::ResidentKeyRequirement::kDiscouraged:
      return std::u16string();
    case device::ResidentKeyRequirement::kPreferred:
      return l10n_util::GetStringUTF16(
          IDS_WEBAUTHN_RESIDENT_KEY_PREFERRED_PRIVACY);
    case device::ResidentKeyRequirement::kRequired:
      return l10n_util::GetStringUTF16(IDS_WEBAUTHN_RESIDENT_KEY_PRIVACY);
  }
  NOTREACHED();
}

// Return a warning about attestation if attestation was requested, otherwise
// return an empty string.
std::u16string PossibleAttestationWarning(
    AuthenticatorRequestDialogModel* dialog_model) {
  if (!dialog_model->attestation_conveyance_preference) {
    return std::u16string();
  }

  switch (*dialog_model->attestation_conveyance_preference) {
    case device::AttestationConveyancePreference::kNone:
      return std::u16string();
    case device::AttestationConveyancePreference::kIndirect:
    case device::AttestationConveyancePreference::kDirect:
      return l10n_util::GetStringFUTF16(
          IDS_WEBAUTHN_ATTESTATION_WARNING,
          AuthenticatorSheetModelBase::GetRelyingPartyIdStringLabel(
              dialog_model));
    case device::AttestationConveyancePreference::
        kEnterpriseIfRPListedOnAuthenticator:
    case device::AttestationConveyancePreference::kEnterpriseApprovedByBrowser:
      return l10n_util::GetStringFUTF16(
          IDS_WEBAUTHN_ENTERPRISE_ATTESTATION_WARNING,
          AuthenticatorSheetModelBase::GetRelyingPartyIdStringLabel(
              dialog_model));
  }
}

}  // namespace

// AuthenticatorSheetModelBase ------------------------------------------------

AuthenticatorSheetModelBase::AuthenticatorSheetModelBase(
    AuthenticatorRequestDialogModel* dialog_model)
    : dialog_model_(dialog_model) {
  DCHECK(dialog_model);
  dialog_model_->observers.AddObserver(this);
}

AuthenticatorSheetModelBase::AuthenticatorSheetModelBase(
    AuthenticatorRequestDialogModel* dialog_model,
    OtherMechanismButtonVisibility other_mechanism_button_visibility)
    : AuthenticatorSheetModelBase(dialog_model) {
  other_mechanism_button_visibility_ = other_mechanism_button_visibility;
}

AuthenticatorSheetModelBase::~AuthenticatorSheetModelBase() {
  if (dialog_model_) {
    dialog_model_->observers.RemoveObserver(this);
    dialog_model_ = nullptr;
  }
}

// static
int AuthenticatorSheetModelBase::GetPreferredContentWidth() {
  views::LayoutProvider* layout_provider = views::LayoutProvider::Get();
  return layout_provider->GetDistanceMetric(
             views::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH) -
         layout_provider->GetInsetsMetric(views::INSETS_DIALOG).width() -
         layout_provider->GetInsetsMetric(views::INSETS_DIALOG_TITLE).width();
}

// static
std::u16string AuthenticatorSheetModelBase::GetRelyingPartyIdString(
    const AuthenticatorRequestDialogModel* dialog_model,
    gfx::FontList font_list) {
  return webauthn_ui_helpers::RpIdToElidedHost(
      dialog_model->relying_party_id, GetPreferredContentWidth(), font_list);
}

// static
std::u16string AuthenticatorSheetModelBase::GetRelyingPartyIdStringLabel(
    const AuthenticatorRequestDialogModel* dialog_model) {
  gfx::FontList font_list = views::TypographyProvider::Get().GetFont(
      views::style::CONTEXT_LABEL, views::style::STYLE_PRIMARY);
  return AuthenticatorSheetModelBase::GetRelyingPartyIdString(dialog_model,
                                                              font_list);
}

// static
std::u16string AuthenticatorSheetModelBase::GetRelyingPartyIdStringTitle(
    const AuthenticatorRequestDialogModel* dialog_model) {
  gfx::FontList font_list = views::TypographyProvider::Get().GetFont(
      views::style::CONTEXT_DIALOG_TITLE, views::style::STYLE_HEADLINE_4);
  return AuthenticatorSheetModelBase::GetRelyingPartyIdString(dialog_model,
                                                              font_list);
}

bool AuthenticatorSheetModelBase::IsActivityIndicatorVisible() const {
  return dialog_model_->ui_disabled_;
}

bool AuthenticatorSheetModelBase::IsCancelButtonVisible() const {
  return true;
}

bool AuthenticatorSheetModelBase::IsOtherMechanismButtonVisible() const {
  return other_mechanism_button_visibility_ ==
             OtherMechanismButtonVisibility::kVisible &&
         dialog_model_ && dialog_model_->mechanisms.size() > 1;
}

std::u16string AuthenticatorSheetModelBase::GetOtherMechanismButtonLabel()
    const {
  switch (dialog_model()->request_type) {
    case device::FidoRequestType::kMakeCredential:
      return l10n_util::GetStringUTF16(IDS_WEBAUTHN_SAVE_ANOTHER_WAY);
    case device::FidoRequestType::kGetAssertion:
      return l10n_util::GetStringUTF16(IDS_WEBAUTHN_USE_A_DIFFERENT_PASSKEY);
  }
}

std::u16string AuthenticatorSheetModelBase::GetCancelButtonLabel() const {
  return l10n_util::GetStringUTF16(IDS_CANCEL);
}

AuthenticatorSheetModelBase::AcceptButtonState
AuthenticatorSheetModelBase::GetAcceptButtonState() const {
  return AcceptButtonState::kNotVisible;
}

std::u16string AuthenticatorSheetModelBase::GetAcceptButtonLabel() const {
  return std::u16string();
}

void AuthenticatorSheetModelBase::OnBack() {
  if (dialog_model()) {
    dialog_model()->StartOver();
  }
}

void AuthenticatorSheetModelBase::OnAccept() {
  NOTREACHED();
}

void AuthenticatorSheetModelBase::OnCancel() {
  if (dialog_model()) {
    webauthn::user_actions::RecordCancelClick();
    dialog_model()->CancelAuthenticatorRequest();
  }
}

void AuthenticatorSheetModelBase::OnModelDestroyed(
    AuthenticatorRequestDialogModel* model) {
  DCHECK(model == dialog_model_);
  dialog_model_ = nullptr;
}

// AuthenticatorMechanismSelectorSheetModel -----------------------------------

AuthenticatorMechanismSelectorSheetModel::
    AuthenticatorMechanismSelectorSheetModel(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model) {
  lottie_illustrations_.emplace(IDR_WEBAUTHN_PASSKEY_LIGHT,
                                IDR_WEBAUTHN_PASSKEY_DARK);
  webauthn::user_actions::RecordMultipleOptionsShown(
      dialog_model->mechanisms, dialog_model->request_type);
}

std::u16string AuthenticatorMechanismSelectorSheetModel::GetStepTitle() const {
  CHECK_EQ(dialog_model()->request_type,
           device::FidoRequestType::kMakeCredential);
  return l10n_util::GetStringFUTF16(
      IDS_WEBAUTHN_CREATE_PASSKEY_CHOOSE_DEVICE_TITLE,
      GetRelyingPartyIdStringTitle(dialog_model()));
}

std::u16string AuthenticatorMechanismSelectorSheetModel::GetStepDescription()
    const {
  return u"";
}

// AuthenticatorInsertAndActivateUsbSheetModel ----------------------

AuthenticatorInsertAndActivateUsbSheetModel::
    AuthenticatorInsertAndActivateUsbSheetModel(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kVisible) {
  webauthn::user_actions::RecordSecurityKeyDialogShown(
      dialog_model->request_type);
  vector_illustrations_.emplace(kPasskeyUsbCustomIcon,
                                kPasskeyUsbDarkCustomIcon);
}

bool AuthenticatorInsertAndActivateUsbSheetModel::IsActivityIndicatorVisible()
    const {
  return true;
}

std::u16string AuthenticatorInsertAndActivateUsbSheetModel::GetStepTitle()
    const {
  return l10n_util::GetStringFUTF16(
      IDS_WEBAUTHN_GENERIC_TITLE, GetRelyingPartyIdStringTitle(dialog_model()));
}

std::u16string AuthenticatorInsertAndActivateUsbSheetModel::GetStepDescription()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_USB_ACTIVATE_DESCRIPTION);
}

std::vector<std::u16string>
AuthenticatorInsertAndActivateUsbSheetModel::GetAdditionalDescriptions() const {
  return {PossibleAttestationWarning(dialog_model()),
          PossibleResidentKeyWarning(dialog_model())};
}

// AuthenticatorTimeoutErrorModel ---------------------------------------------

AuthenticatorTimeoutErrorModel::AuthenticatorTimeoutErrorModel(
    AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model) {
  vector_illustrations_.emplace(kPasskeyErrorCustomIcon,
                                kPasskeyErrorDarkCustomIcon);
}

std::u16string AuthenticatorTimeoutErrorModel::GetCancelButtonLabel() const {
  return l10n_util::GetStringUTF16(IDS_CLOSE);
}

std::u16string AuthenticatorTimeoutErrorModel::GetStepTitle() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_GENERIC_TITLE);
}

std::u16string AuthenticatorTimeoutErrorModel::GetStepDescription() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_TIMEOUT_DESCRIPTION);
}

// AuthenticatorNoAvailableTransportsErrorModel -------------------------------

AuthenticatorNoAvailableTransportsErrorModel::
    AuthenticatorNoAvailableTransportsErrorModel(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model) {
  vector_illustrations_.emplace(kPasskeyErrorCustomIcon,
                                kPasskeyErrorDarkCustomIcon);
}

std::u16string
AuthenticatorNoAvailableTransportsErrorModel::GetCancelButtonLabel() const {
  return l10n_util::GetStringUTF16(IDS_CLOSE);
}

std::u16string AuthenticatorNoAvailableTransportsErrorModel::GetStepTitle()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_MISSING_CAPABILITY_TITLE);
}

std::u16string
AuthenticatorNoAvailableTransportsErrorModel::GetStepDescription() const {
  return l10n_util::GetStringFUTF16(
      IDS_WEBAUTHN_ERROR_MISSING_CAPABILITY_DESC,
      GetRelyingPartyIdStringLabel(dialog_model()));
}

// AuthenticatorNoPasskeysErrorModel ------------------------------------------

AuthenticatorNoPasskeysErrorModel::AuthenticatorNoPasskeysErrorModel(
    AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model) {
  vector_illustrations_.emplace(kPasskeyErrorCustomIcon,
                                kPasskeyErrorDarkCustomIcon);
}

std::u16string AuthenticatorNoPasskeysErrorModel::GetCancelButtonLabel() const {
  return l10n_util::GetStringUTF16(IDS_CLOSE);
}
std::u16string AuthenticatorNoPasskeysErrorModel::GetStepTitle() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_NO_PASSKEYS_TITLE);
}

std::u16string AuthenticatorNoPasskeysErrorModel::GetStepDescription() const {
  return l10n_util::GetStringFUTF16(
      IDS_WEBAUTHN_ERROR_NO_PASSKEYS_DESCRIPTION,
      GetRelyingPartyIdStringLabel(dialog_model()));
}

// AuthenticatorGpmDisabledErrorModel ------------------------------------------

AuthenticatorGpmDisabledErrorModel::AuthenticatorGpmDisabledErrorModel(
    AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model) {
  vector_illustrations_.emplace(kPasskeyErrorCustomIcon,
                                kPasskeyErrorDarkCustomIcon);
}

std::u16string AuthenticatorGpmDisabledErrorModel::GetCancelButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_CLOSE);
}

std::u16string AuthenticatorGpmDisabledErrorModel::GetStepTitle() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_GPM_DISABLED_TITLE);
}

std::u16string AuthenticatorGpmDisabledErrorModel::GetStepDescription() const {
  return l10n_util::GetStringFUTF16(
      IDS_WEBAUTHN_ERROR_GPM_DISABLED_DESCRIPTION,
      GetRelyingPartyIdStringLabel(dialog_model()));
}

bool AuthenticatorGpmDisabledErrorModel::IsGpmSettingsButtonVisible() const {
  return true;
}

void AuthenticatorGpmDisabledErrorModel::OnOpenGpmSettingsButtonPressed() {
  dialog_model()->OpenGpmSettings();
}

// AuthenticatorNotRegisteredErrorModel ---------------------------------------

AuthenticatorNotRegisteredErrorModel::AuthenticatorNotRegisteredErrorModel(
    AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model) {
  vector_illustrations_.emplace(kPasskeyErrorCustomIcon,
                                kPasskeyErrorDarkCustomIcon);
}

std::u16string AuthenticatorNotRegisteredErrorModel::GetCancelButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_CLOSE);
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorNotRegisteredErrorModel::GetAcceptButtonState() const {
  return dialog_model()->offer_try_again_in_ui ? AcceptButtonState::kEnabled
                                               : AcceptButtonState::kNotVisible;
}

std::u16string AuthenticatorNotRegisteredErrorModel::GetAcceptButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_RETRY);
}

std::u16string AuthenticatorNotRegisteredErrorModel::GetStepTitle() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_WRONG_KEY_TITLE);
}

std::u16string AuthenticatorNotRegisteredErrorModel::GetStepDescription()
    const {
  return l10n_util::GetStringUTF16(
      IDS_WEBAUTHN_ERROR_WRONG_KEY_SIGN_DESCRIPTION);
}

void AuthenticatorNotRegisteredErrorModel::OnAccept() {
  dialog_model()->StartOver();
}

// AuthenticatorAlreadyRegisteredErrorModel -----------------------------------

AuthenticatorAlreadyRegisteredErrorModel::
    AuthenticatorAlreadyRegisteredErrorModel(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model) {
  vector_illustrations_.emplace(kPasskeyErrorCustomIcon,
                                kPasskeyErrorDarkCustomIcon);
}

std::u16string AuthenticatorAlreadyRegisteredErrorModel::GetCancelButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_CLOSE);
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorAlreadyRegisteredErrorModel::GetAcceptButtonState() const {
  return dialog_model()->offer_try_again_in_ui ? AcceptButtonState::kEnabled
                                               : AcceptButtonState::kNotVisible;
}

std::u16string AuthenticatorAlreadyRegisteredErrorModel::GetAcceptButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_RETRY);
}

std::u16string AuthenticatorAlreadyRegisteredErrorModel::GetStepTitle() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_WRONG_DEVICE_TITLE);
}

std::u16string AuthenticatorAlreadyRegisteredErrorModel::GetStepDescription()
    const {
  return l10n_util::GetStringUTF16(
      IDS_WEBAUTHN_ERROR_WRONG_DEVICE_REGISTER_DESCRIPTION);
}

void AuthenticatorAlreadyRegisteredErrorModel::OnAccept() {
  dialog_model()->StartOver();
}

// AuthenticatorInternalUnrecognizedErrorSheetModel ---------------------------

AuthenticatorInternalUnrecognizedErrorSheetModel::
    AuthenticatorInternalUnrecognizedErrorSheetModel(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model) {
  vector_illustrations_.emplace(kPasskeyErrorCustomIcon,
                                kPasskeyErrorDarkCustomIcon);
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorInternalUnrecognizedErrorSheetModel::GetAcceptButtonState() const {
  return dialog_model()->offer_try_again_in_ui ? AcceptButtonState::kEnabled
                                               : AcceptButtonState::kNotVisible;
}

std::u16string
AuthenticatorInternalUnrecognizedErrorSheetModel::GetAcceptButtonLabel() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_RETRY);
}

std::u16string AuthenticatorInternalUnrecognizedErrorSheetModel::GetStepTitle()
    const {
  return l10n_util::GetStringUTF16(
      IDS_WEBAUTHN_ERROR_INTERNAL_UNRECOGNIZED_TITLE);
}

std::u16string
AuthenticatorInternalUnrecognizedErrorSheetModel::GetStepDescription() const {
  return l10n_util::GetStringUTF16(
      IDS_WEBAUTHN_ERROR_INTERNAL_UNRECOGNIZED_DESCRIPTION);
}

void AuthenticatorInternalUnrecognizedErrorSheetModel::OnAccept() {
  dialog_model()->StartOver();
}

// AuthenticatorChallengeFetchErrorModel
// ---------------------------------------------

AuthenticatorChallengeFetchErrorModel::AuthenticatorChallengeFetchErrorModel(
    AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model) {
  vector_illustrations_.emplace(kPasskeyErrorCustomIcon,
                                kPasskeyErrorDarkCustomIcon);
}

std::u16string AuthenticatorChallengeFetchErrorModel::GetCancelButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_CLOSE);
}

std::u16string AuthenticatorChallengeFetchErrorModel::GetStepTitle() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_GENERIC_TITLE);
}

std::u16string AuthenticatorChallengeFetchErrorModel::GetStepDescription()
    const {
  // TODO(https://crbug.com/381219428): Get an approved string for this dialog.
  return u"An error occurred trying to process this request. (UT)";
}

// AuthenticatorBlePowerOnManualSheetModel ------------------------------------

AuthenticatorBlePowerOnManualSheetModel::
    AuthenticatorBlePowerOnManualSheetModel(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kVisible) {
  vector_illustrations_.emplace(kPasskeyErrorBluetoothCustomIcon,
                                kPasskeyErrorBluetoothDarkCustomIcon);
}

std::u16string AuthenticatorBlePowerOnManualSheetModel::GetStepTitle() const {
  return l10n_util::GetStringUTF16(
      IDS_WEBAUTHN_BLUETOOTH_POWER_ON_MANUAL_TITLE);
}

std::u16string AuthenticatorBlePowerOnManualSheetModel::GetStepDescription()
    const {
  return l10n_util::GetStringUTF16(
      IDS_WEBAUTHN_BLUETOOTH_POWER_ON_MANUAL_DESCRIPTION);
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorBlePowerOnManualSheetModel::GetAcceptButtonState() const {
  return dialog_model()->ble_adapter_is_powered ? AcceptButtonState::kEnabled
                                                : AcceptButtonState::kDisabled;
}

std::u16string AuthenticatorBlePowerOnManualSheetModel::GetAcceptButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_BLUETOOTH_POWER_ON_MANUAL_NEXT);
}

void AuthenticatorBlePowerOnManualSheetModel::OnBluetoothPoweredStateChanged() {
  dialog_model()->OnSheetModelChanged();
}

void AuthenticatorBlePowerOnManualSheetModel::OnAccept() {
  dialog_model()->ContinueWithFlowAfterBleAdapterPowered();
}

// AuthenticatorBlePowerOnAutomaticSheetModel
// ------------------------------------

AuthenticatorBlePowerOnAutomaticSheetModel::
    AuthenticatorBlePowerOnAutomaticSheetModel(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kVisible) {
  vector_illustrations_.emplace(kPasskeyErrorBluetoothCustomIcon,
                                kPasskeyErrorBluetoothDarkCustomIcon);
}

bool AuthenticatorBlePowerOnAutomaticSheetModel::IsActivityIndicatorVisible()
    const {
  return busy_powering_on_ble_;
}

std::u16string AuthenticatorBlePowerOnAutomaticSheetModel::GetStepTitle()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_BLUETOOTH_POWER_ON_AUTO_TITLE);
}

std::u16string AuthenticatorBlePowerOnAutomaticSheetModel::GetStepDescription()
    const {
  return l10n_util::GetStringUTF16(
      IDS_WEBAUTHN_BLUETOOTH_POWER_ON_AUTO_DESCRIPTION);
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorBlePowerOnAutomaticSheetModel::GetAcceptButtonState() const {
  return busy_powering_on_ble_ ? AcceptButtonState::kDisabled
                               : AcceptButtonState::kEnabled;
}

std::u16string
AuthenticatorBlePowerOnAutomaticSheetModel::GetAcceptButtonLabel() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_BLUETOOTH_POWER_ON_AUTO_NEXT);
}

void AuthenticatorBlePowerOnAutomaticSheetModel::OnAccept() {
  busy_powering_on_ble_ = true;
  dialog_model()->OnSheetModelChanged();
  dialog_model()->PowerOnBleAdapter();
}

#if BUILDFLAG(IS_MAC)

// AuthenticatorBlePermissionMacSheetModel
// ------------------------------------

AuthenticatorBlePermissionMacSheetModel::
    AuthenticatorBlePermissionMacSheetModel(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kVisible) {
  vector_illustrations_.emplace(kPasskeyErrorBluetoothCustomIcon,
                                kPasskeyErrorBluetoothDarkCustomIcon);
}

std::u16string AuthenticatorBlePermissionMacSheetModel::GetStepTitle() const {
  // An empty title causes the title View to be omitted.
  return u"";
}

std::u16string AuthenticatorBlePermissionMacSheetModel::GetStepDescription()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_BLUETOOTH_PERMISSION);
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorBlePermissionMacSheetModel::GetAcceptButtonState() const {
  return AcceptButtonState::kEnabled;
}

bool AuthenticatorBlePermissionMacSheetModel::IsCancelButtonVisible() const {
  return true;
}

std::u16string AuthenticatorBlePermissionMacSheetModel::GetAcceptButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_OPEN_SETTINGS_LINK);
}

void AuthenticatorBlePermissionMacSheetModel::OnAccept() {
  dialog_model()->OpenBlePreferences();
}

// AuthenticatorTouchIdSheetModel
// ------------------------------------

AuthenticatorTouchIdSheetModel::AuthenticatorTouchIdSheetModel(
    AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kVisible) {
  webauthn::user_actions::RecordGpmTouchIdDialogShown(
      dialog_model->request_type);
}

std::u16string AuthenticatorTouchIdSheetModel::GetStepTitle() const {
  const std::u16string rp_id = GetRelyingPartyIdStringTitle(dialog_model());
  std::optional<int> id;
  switch (dialog_model()->request_type) {
    case device::FidoRequestType::kMakeCredential:
      id = IDS_WEBAUTHN_GPM_CREATE_PASSKEY_TITLE;
      break;
    case device::FidoRequestType::kGetAssertion:
      id = dialog_model()->ui_presentation == UIPresentation::kModalImmediate
               ? IDS_WEBAUTHN_SIGN_IN_TO_WEBSITE_DIALOG_TITLE
               : IDS_WEBAUTHN_CHOOSE_PASSKEY_FOR_RP_TITLE;
      break;
  }
  CHECK(id.has_value());
  return l10n_util::GetStringFUTF16(id.value(), rp_id);
}

std::u16string AuthenticatorTouchIdSheetModel::GetStepDescription() const {
  switch (dialog_model()->request_type) {
    case device::FidoRequestType::kMakeCredential:
      return l10n_util::GetStringFUTF16(
          IDS_WEBAUTHN_GPM_CREATE_PASSKEY_DESC,
          base::UTF8ToUTF16(dialog_model()->GetGpmAccountEmail()));

    case device::FidoRequestType::kGetAssertion:
      return dialog_model()->ui_presentation == UIPresentation::kModalImmediate
                 ? std::u16string()
                 : l10n_util::GetStringFUTF16(
                       IDS_WEBAUTHN_TOUCH_ID_ASSERTION_DESC,
                       GetRelyingPartyIdStringLabel(dialog_model()));
  }
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorTouchIdSheetModel::GetAcceptButtonState() const {
  // Visible only if biometrics aren't available (fallback to password)
  return !device::fido::mac::DeviceHasBiometricsAvailable()
             ? AcceptButtonState::kEnabled
             : AcceptButtonState::kNotVisible;
}

bool AuthenticatorTouchIdSheetModel::IsCancelButtonVisible() const {
  return true;
}

std::u16string AuthenticatorTouchIdSheetModel::GetAcceptButtonLabel() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_TOUCH_ID_ENTER_PASSWORD);
}

std::u16string AuthenticatorTouchIdSheetModel::GetCancelButtonLabel() const {
  return dialog_model()->ui_presentation == UIPresentation::kModalImmediate
             ? l10n_util::GetStringUTF16(IDS_SIGNIN_ACCESSIBLE_CLOSE_BUTTON)
             : l10n_util::GetStringUTF16(IDS_CANCEL);
}

void AuthenticatorTouchIdSheetModel::OnAccept() {
  if (touch_id_completed_) {
    return;
  }
  webauthn::user_actions::RecordAcceptClick();
  touch_id_completed_ = true;
  dialog_model()->OnGPMTouchIDComplete(false);
}

void AuthenticatorTouchIdSheetModel::OnTouchIDSensorTapped(
    std::optional<webauthn::LocalAuthenticationToken> local_auth_token) {
  // Ignore Touch ID ceremony status after the user has completed the ceremony.
  if (touch_id_completed_) {
    return;
  }
  if (!local_auth_token) {
    // Authentication failed. Update the button status and rebuild the sheet,
    // which will restart the Touch ID request if the sensor is not softlocked
    // or display a padlock icon if it is.
    dialog_model()->OnSheetModelChanged();
    return;
  }
  touch_id_completed_ = true;
  dialog_model()->local_auth_token = std::move(local_auth_token);
  dialog_model()->OnGPMTouchIDComplete(true);
}

#endif  // IS_MAC

// AuthenticatorOffTheRecordInterstitialSheetModel
// -----------------------------------------

AuthenticatorOffTheRecordInterstitialSheetModel::
    AuthenticatorOffTheRecordInterstitialSheetModel(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model) {
  // TODO(crbug.com/40237082): Add more specific illustration once available.
  // The "error" graphic is a large question mark, so it looks visually very
  // similar.
  vector_illustrations_.emplace(kPasskeyErrorCustomIcon,
                                kPasskeyErrorDarkCustomIcon);
}

std::u16string AuthenticatorOffTheRecordInterstitialSheetModel::GetStepTitle()
    const {
  return l10n_util::GetStringFUTF16(
      IDS_WEBAUTHN_PLATFORM_AUTHENTICATOR_OFF_THE_RECORD_INTERSTITIAL_TITLE,
      GetRelyingPartyIdStringTitle(dialog_model()));
}

std::u16string
AuthenticatorOffTheRecordInterstitialSheetModel::GetStepDescription() const {
  return l10n_util::GetStringUTF16(
      IDS_WEBAUTHN_PLATFORM_AUTHENTICATOR_OFF_THE_RECORD_INTERSTITIAL_DESCRIPTION);
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorOffTheRecordInterstitialSheetModel::GetAcceptButtonState() const {
  return AcceptButtonState::kEnabled;
}

std::u16string
AuthenticatorOffTheRecordInterstitialSheetModel::GetAcceptButtonLabel() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CONTINUE);
}

void AuthenticatorOffTheRecordInterstitialSheetModel::OnAccept() {
  dialog_model()->OnOffTheRecordInterstitialAccepted();
}

std::u16string
AuthenticatorOffTheRecordInterstitialSheetModel::GetCancelButtonLabel() const {
  return l10n_util::GetStringUTF16(
      IDS_WEBAUTHN_PLATFORM_AUTHENTICATOR_OFF_THE_RECORD_INTERSTITIAL_DENY);
}

// AuthenticatorClientPinEntrySheetModel
// -----------------------------------------

AuthenticatorClientPinEntrySheetModel::AuthenticatorClientPinEntrySheetModel(
    AuthenticatorRequestDialogModel* dialog_model,
    Mode mode,
    device::pin::PINEntryError error)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kVisible),
      mode_(mode) {
  vector_illustrations_.emplace(kPasskeyUsbCustomIcon,
                                kPasskeyUsbDarkCustomIcon);
  switch (error) {
    case device::pin::PINEntryError::kNoError:
      break;
    case device::pin::PINEntryError::kInternalUvLocked:
      error_ = l10n_util::GetStringUTF16(IDS_WEBAUTHN_UV_ERROR_LOCKED);
      break;
    case device::pin::PINEntryError::kInvalidCharacters:
      error_ = l10n_util::GetStringUTF16(
          IDS_WEBAUTHN_PIN_ENTRY_ERROR_INVALID_CHARACTERS);
      break;
    case device::pin::PINEntryError::kSameAsCurrentPIN:
      error_ = l10n_util::GetStringUTF16(
          IDS_WEBAUTHN_PIN_ENTRY_ERROR_SAME_AS_CURRENT);
      break;
    case device::pin::PINEntryError::kTooShort:
      error_ = l10n_util::GetPluralStringFUTF16(
          IDS_WEBAUTHN_PIN_ENTRY_ERROR_TOO_SHORT, dialog_model->min_pin_length);
      break;
    case device::pin::PINEntryError::kWrongPIN:
      std::optional<int> attempts = dialog_model->pin_attempts;
      error_ =
          attempts && *attempts <= 3
              ? l10n_util::GetPluralStringFUTF16(
                    IDS_WEBAUTHN_PIN_ENTRY_ERROR_FAILED_RETRIES, *attempts)
              : l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_ERROR_FAILED);
      break;
  }
}

AuthenticatorClientPinEntrySheetModel::
    ~AuthenticatorClientPinEntrySheetModel() = default;

void AuthenticatorClientPinEntrySheetModel::SetPinCode(
    std::u16string pin_code) {
  pin_code_ = std::move(pin_code);
}

void AuthenticatorClientPinEntrySheetModel::SetPinConfirmation(
    std::u16string pin_confirmation) {
  DCHECK(mode_ == Mode::kPinSetup || mode_ == Mode::kPinChange);
  pin_confirmation_ = std::move(pin_confirmation);
}

std::u16string AuthenticatorClientPinEntrySheetModel::GetStepTitle() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_TITLE);
}

std::u16string AuthenticatorClientPinEntrySheetModel::GetStepDescription()
    const {
  switch (mode_) {
    case Mode::kPinChange:
      return l10n_util::GetStringUTF16(IDS_WEBAUTHN_FORCE_PIN_CHANGE);
    case Mode::kPinEntry:
      return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_DESCRIPTION);
    case Mode::kPinSetup:
      return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_SETUP_DESCRIPTION);
  }
}

std::u16string AuthenticatorClientPinEntrySheetModel::GetError() const {
  return error_;
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorClientPinEntrySheetModel::GetAcceptButtonState() const {
  return AcceptButtonState::kEnabled;
}

std::u16string AuthenticatorClientPinEntrySheetModel::GetAcceptButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_NEXT);
}

void AuthenticatorClientPinEntrySheetModel::OnAccept() {
  if ((mode_ == Mode::kPinChange || mode_ == Mode::kPinSetup) &&
      pin_code_ != pin_confirmation_) {
    error_ = l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_ERROR_MISMATCH);
    dialog_model()->OnSheetModelChanged();
    return;
  }

  if (dialog_model()) {
    dialog_model()->OnHavePIN(pin_code_);
  }
}

bool AuthenticatorClientPinEntrySheetModel::IsOtherMechanismButtonVisible()
    const {
  // Always allow restarting the request to select a different security key or
  // hybrid authenticator.
  return true;
}

// AuthenticatorClientPinTapAgainSheetModel ----------------------

AuthenticatorClientPinTapAgainSheetModel::
    AuthenticatorClientPinTapAgainSheetModel(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model) {
  vector_illustrations_.emplace(kPasskeyUsbCustomIcon,
                                kPasskeyUsbDarkCustomIcon);
}

AuthenticatorClientPinTapAgainSheetModel::
    ~AuthenticatorClientPinTapAgainSheetModel() = default;

bool AuthenticatorClientPinTapAgainSheetModel::IsActivityIndicatorVisible()
    const {
  return true;
}

std::u16string AuthenticatorClientPinTapAgainSheetModel::GetStepTitle() const {
  return l10n_util::GetStringFUTF16(
      IDS_WEBAUTHN_GENERIC_TITLE, GetRelyingPartyIdStringTitle(dialog_model()));
}

std::u16string AuthenticatorClientPinTapAgainSheetModel::GetStepDescription()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_TAP_AGAIN_DESCRIPTION);
}

std::vector<std::u16string>
AuthenticatorClientPinTapAgainSheetModel::GetAdditionalDescriptions() const {
  return {PossibleAttestationWarning(dialog_model()),
          PossibleResidentKeyWarning(dialog_model())};
}

// AuthenticatorBioEnrollmentSheetModel ----------------------------------

// No illustration since the content already has a large animated
// fingerprint icon.
AuthenticatorBioEnrollmentSheetModel::AuthenticatorBioEnrollmentSheetModel(
    AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model) {}

AuthenticatorBioEnrollmentSheetModel::~AuthenticatorBioEnrollmentSheetModel() =
    default;

bool AuthenticatorBioEnrollmentSheetModel::IsActivityIndicatorVisible() const {
  return !HasBioSamplesRemaining();
}

std::u16string AuthenticatorBioEnrollmentSheetModel::GetStepTitle() const {
  return l10n_util::GetStringUTF16(
      IDS_SETTINGS_SECURITY_KEYS_BIO_ENROLLMENT_ADD_TITLE);
}

std::u16string AuthenticatorBioEnrollmentSheetModel::GetStepDescription()
    const {
  return HasBioSamplesRemaining()
             ? l10n_util::GetStringUTF16(
                   IDS_SETTINGS_SECURITY_KEYS_BIO_ENROLLMENT_ENROLLING_COMPLETE_LABEL)
             : l10n_util::GetStringUTF16(
                   IDS_SETTINGS_SECURITY_KEYS_BIO_ENROLLMENT_ENROLLING_LABEL);
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorBioEnrollmentSheetModel::GetAcceptButtonState() const {
  return HasBioSamplesRemaining() ? AcceptButtonState::kEnabled
                                  : AcceptButtonState::kNotVisible;
}

std::u16string AuthenticatorBioEnrollmentSheetModel::GetAcceptButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_NEXT);
}

bool AuthenticatorBioEnrollmentSheetModel::IsCancelButtonVisible() const {
  return !HasBioSamplesRemaining();
}

std::u16string AuthenticatorBioEnrollmentSheetModel::GetCancelButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_INLINE_ENROLLMENT_CANCEL_LABEL);
}

void AuthenticatorBioEnrollmentSheetModel::OnAccept() {
  dialog_model()->OnBioEnrollmentDone();
}

void AuthenticatorBioEnrollmentSheetModel::OnCancel() {
  OnAccept();
}

bool AuthenticatorBioEnrollmentSheetModel::HasBioSamplesRemaining() const {
  return dialog_model()->bio_samples_remaining &&
         dialog_model()->bio_samples_remaining <= 0;
}

// AuthenticatorRetryUvSheetModel -------------------------------------

AuthenticatorRetryUvSheetModel::AuthenticatorRetryUvSheetModel(
    AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kVisible) {
  vector_illustrations_.emplace(kPasskeyFingerprintCustomIcon,
                                kPasskeyFingerprintDarkCustomIcon);
}

AuthenticatorRetryUvSheetModel::~AuthenticatorRetryUvSheetModel() = default;

bool AuthenticatorRetryUvSheetModel::IsActivityIndicatorVisible() const {
  return true;
}

std::u16string AuthenticatorRetryUvSheetModel::GetStepTitle() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_UV_RETRY_TITLE);
}

std::u16string AuthenticatorRetryUvSheetModel::GetStepDescription() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_UV_RETRY_DESCRIPTION);
}

std::u16string AuthenticatorRetryUvSheetModel::GetError() const {
  int attempts = *dialog_model()->uv_attempts;
  if (attempts > 3) {
    return std::u16string();
  }
  return l10n_util::GetPluralStringFUTF16(
      IDS_WEBAUTHN_UV_RETRY_ERROR_FAILED_RETRIES, attempts);
}

// AuthenticatorGenericErrorSheetModel -----------------------------------

// static
std::unique_ptr<AuthenticatorGenericErrorSheetModel>
AuthenticatorGenericErrorSheetModel::ForClientPinErrorSoftBlock(
    AuthenticatorRequestDialogModel* dialog_model) {
  return base::WrapUnique(new AuthenticatorGenericErrorSheetModel(
      dialog_model, l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_GENERIC_TITLE),
      l10n_util::GetStringUTF16(
          IDS_WEBAUTHN_CLIENT_PIN_SOFT_BLOCK_DESCRIPTION)));
}

// static
std::unique_ptr<AuthenticatorGenericErrorSheetModel>
AuthenticatorGenericErrorSheetModel::ForClientPinErrorHardBlock(
    AuthenticatorRequestDialogModel* dialog_model) {
  return base::WrapUnique(new AuthenticatorGenericErrorSheetModel(
      dialog_model, l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_GENERIC_TITLE),
      l10n_util::GetStringUTF16(
          IDS_WEBAUTHN_CLIENT_PIN_HARD_BLOCK_DESCRIPTION)));
}

// static
std::unique_ptr<AuthenticatorGenericErrorSheetModel>
AuthenticatorGenericErrorSheetModel::ForClientPinErrorAuthenticatorRemoved(
    AuthenticatorRequestDialogModel* dialog_model) {
  return base::WrapUnique(new AuthenticatorGenericErrorSheetModel(
      dialog_model, l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_GENERIC_TITLE),
      l10n_util::GetStringUTF16(
          IDS_WEBAUTHN_CLIENT_PIN_AUTHENTICATOR_REMOVED_DESCRIPTION)));
}

// static
std::unique_ptr<AuthenticatorGenericErrorSheetModel>
AuthenticatorGenericErrorSheetModel::ForMissingCapability(
    AuthenticatorRequestDialogModel* dialog_model) {
  return base::WrapUnique(new AuthenticatorGenericErrorSheetModel(
      dialog_model,
      l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_MISSING_CAPABILITY_TITLE),
      l10n_util::GetStringFUTF16(IDS_WEBAUTHN_ERROR_MISSING_CAPABILITY_DESC,
                                 GetRelyingPartyIdStringLabel(dialog_model))));
}

// static
std::unique_ptr<AuthenticatorGenericErrorSheetModel>
AuthenticatorGenericErrorSheetModel::ForStorageFull(
    AuthenticatorRequestDialogModel* dialog_model) {
  return base::WrapUnique(new AuthenticatorGenericErrorSheetModel(
      dialog_model,
      l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_MISSING_CAPABILITY_TITLE),
      l10n_util::GetStringUTF16(IDS_WEBAUTHN_STORAGE_FULL_DESC)));
}

std::unique_ptr<AuthenticatorGenericErrorSheetModel>
AuthenticatorGenericErrorSheetModel::ForWindowsHelloNotEnabled(
    AuthenticatorRequestDialogModel* dialog_model) {
  return base::WrapUnique(new AuthenticatorGenericErrorSheetModel(
      dialog_model,
      l10n_util::GetStringUTF16(IDS_WEBAUTHN_WINDOWS_HELLO_NOT_ENABLED_TITLE),
      l10n_util::GetStringUTF16(
          IDS_WEBAUTHN_WINDOWS_HELLO_NOT_ENABLED_DESCRIPTION)));
}

AuthenticatorGenericErrorSheetModel::AuthenticatorGenericErrorSheetModel(
    AuthenticatorRequestDialogModel* dialog_model,
    std::u16string title,
    std::u16string description)
    : AuthenticatorSheetModelBase(dialog_model),
      title_(std::move(title)),
      description_(std::move(description)) {
  vector_illustrations_.emplace(kPasskeyErrorCustomIcon,
                                kPasskeyErrorDarkCustomIcon);
}

std::u16string AuthenticatorGenericErrorSheetModel::GetCancelButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_CLOSE);
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorGenericErrorSheetModel::GetAcceptButtonState() const {
  return dialog_model()->offer_try_again_in_ui ? AcceptButtonState::kEnabled
                                               : AcceptButtonState::kNotVisible;
}

std::u16string AuthenticatorGenericErrorSheetModel::GetAcceptButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_RETRY);
}

std::u16string AuthenticatorGenericErrorSheetModel::GetStepTitle() const {
  return title_;
}

std::u16string AuthenticatorGenericErrorSheetModel::GetStepDescription() const {
  return description_;
}

void AuthenticatorGenericErrorSheetModel::OnAccept() {
  dialog_model()->StartOver();
}

// AuthenticatorResidentCredentialConfirmationSheetView -----------------------

// TODO(crbug.com/40237082): Add more specific illustration once available. The
// "error" graphic is a large question mark, so it looks visually very similar.
AuthenticatorResidentCredentialConfirmationSheetView::
    AuthenticatorResidentCredentialConfirmationSheetView(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model) {
  vector_illustrations_.emplace(kPasskeyErrorCustomIcon,
                                kPasskeyErrorDarkCustomIcon);
}

AuthenticatorResidentCredentialConfirmationSheetView::
    ~AuthenticatorResidentCredentialConfirmationSheetView() = default;

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorResidentCredentialConfirmationSheetView::GetAcceptButtonState()
    const {
  return AcceptButtonState::kEnabled;
}

std::u16string
AuthenticatorResidentCredentialConfirmationSheetView::GetAcceptButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CONTINUE);
}

std::u16string
AuthenticatorResidentCredentialConfirmationSheetView::GetStepTitle() const {
  return l10n_util::GetStringFUTF16(
      IDS_WEBAUTHN_GENERIC_TITLE, GetRelyingPartyIdStringTitle(dialog_model()));
}

std::u16string
AuthenticatorResidentCredentialConfirmationSheetView::GetStepDescription()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_RESIDENT_KEY_PRIVACY);
}

void AuthenticatorResidentCredentialConfirmationSheetView::OnAccept() {
  dialog_model()->OnResidentCredentialConfirmed();
}

// AuthenticatorSelectAccountSheetModel ---------------------------------------

AuthenticatorSelectAccountSheetModel::AuthenticatorSelectAccountSheetModel(
    AuthenticatorRequestDialogModel* dialog_model,
    UserVerificationMode mode)
    : AuthenticatorSheetModelBase(
          dialog_model,
          mode == kPreUserVerification
              ? OtherMechanismButtonVisibility::kVisible
              : OtherMechanismButtonVisibility::kHidden),
      user_verification_mode_(mode) {
  lottie_illustrations_.emplace(IDR_WEBAUTHN_PASSKEY_LIGHT,
                                IDR_WEBAUTHN_PASSKEY_DARK);
}

AuthenticatorSelectAccountSheetModel::~AuthenticatorSelectAccountSheetModel() =
    default;

void AuthenticatorSelectAccountSheetModel::SetCurrentSelection(int selected) {
  DCHECK_LE(0, selected);
  DCHECK_LT(static_cast<size_t>(selected), dialog_model()->creds.size());
  selected_ = selected;
}

void AuthenticatorSelectAccountSheetModel::OnAccept() {
  switch (user_verification_mode_) {
    case kPreUserVerification:
      dialog_model()->OnAccountPreselectedIndex(selected_);
      break;
    case kPostUserVerification:
      dialog_model()->OnAccountSelected(selected_);
      break;
  }
}

std::u16string AuthenticatorSelectAccountSheetModel::GetStepTitle() const {
  if (dialog_model()->creds.size() > 1) {
    return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CHOOSE_PASSKEY_TITLE);
  }
  return l10n_util::GetStringFUTF16(
      IDS_WEBAUTHN_USE_PASSKEY_TITLE,
      GetRelyingPartyIdStringTitle(dialog_model()));
}

std::u16string AuthenticatorSelectAccountSheetModel::GetStepDescription()
    const {
  if (dialog_model()->creds.size() > 1) {
    return l10n_util::GetStringFUTF16(
        IDS_WEBAUTHN_CHOOSE_PASSKEY_BODY,
        GetRelyingPartyIdStringLabel(dialog_model()));
  }
  return u"";
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorSelectAccountSheetModel::GetAcceptButtonState() const {
  return dialog_model()->creds.size() == 1 ? AcceptButtonState::kEnabled
                                           : AcceptButtonState::kNotVisible;
}

std::u16string AuthenticatorSelectAccountSheetModel::GetAcceptButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CONTINUE);
}

// AuthenticatorHybridAndSecurityKeySheetModel --------------------------------

AuthenticatorHybridAndSecurityKeySheetModel::
    AuthenticatorHybridAndSecurityKeySheetModel(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kVisible) {
  webauthn::user_actions::RecordHybridAndSecurityKeyDialogShown(
      dialog_model->request_type);
}

AuthenticatorHybridAndSecurityKeySheetModel::
    ~AuthenticatorHybridAndSecurityKeySheetModel() = default;

std::u16string AuthenticatorHybridAndSecurityKeySheetModel::GetStepTitle()
    const {
  return l10n_util::GetStringUTF16(dialog_model()->show_security_key_on_qr_sheet
                                       ? IDS_WEBAUTHN_PASSKEYS_AND_SECURITY_KEYS
                                       : IDS_WEBAUTHN_PASSKEYS);
}

std::u16string AuthenticatorHybridAndSecurityKeySheetModel::GetStepDescription()
    const {
  return u"";
}

std::u16string
AuthenticatorHybridAndSecurityKeySheetModel::GetOtherMechanismButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_ACCOUNT_SELECTION_BACK);
}

std::optional<std::u16string>
AuthenticatorHybridAndSecurityKeySheetModel::GetAttestationWarning() const {
  if (dialog_model()->show_security_key_on_qr_sheet &&
      (dialog_model()->request_type ==
       device::FidoRequestType::kMakeCredential)) {
    std::u16string warning = PossibleAttestationWarning(dialog_model());
    if (!warning.empty()) {
      return warning;
    }
  }
  return std::nullopt;
}

// AuthenticatorConnectingSheetModel ------------------------------------------

AuthenticatorConnectingSheetModel::AuthenticatorConnectingSheetModel(
    AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kHidden) {
  lottie_illustrations_.emplace(IDR_WEBAUTHN_HYBRID_CONNECTING_LIGHT,
                                IDR_WEBAUTHN_HYBRID_CONNECTING_DARK);
}

AuthenticatorConnectingSheetModel::~AuthenticatorConnectingSheetModel() =
    default;

std::u16string AuthenticatorConnectingSheetModel::GetStepTitle() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CABLEV2_CONNECTING_TITLE);
}

std::u16string AuthenticatorConnectingSheetModel::GetStepDescription() const {
  return u"";
}

// AuthenticatorConnectedSheetModel ------------------------------------------

AuthenticatorConnectedSheetModel::AuthenticatorConnectedSheetModel(
    AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kHidden) {
  vector_illustrations_.emplace(kPasskeyPhoneCustomIcon,
                                kPasskeyPhoneDarkCustomIcon);
}

AuthenticatorConnectedSheetModel::~AuthenticatorConnectedSheetModel() = default;

bool AuthenticatorConnectedSheetModel::IsActivityIndicatorVisible() const {
  return false;
}

std::u16string AuthenticatorConnectedSheetModel::GetStepTitle() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CABLEV2_CONNECTED_DESCRIPTION);
}

std::u16string AuthenticatorConnectedSheetModel::GetStepDescription() const {
  return u"";
}

// AuthenticatorCableErrorSheetModel ------------------------------------------

AuthenticatorCableErrorSheetModel::AuthenticatorCableErrorSheetModel(
    AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kHidden) {
  vector_illustrations_.emplace(kPasskeyErrorCustomIcon,
                                kPasskeyErrorDarkCustomIcon);
}

AuthenticatorCableErrorSheetModel::~AuthenticatorCableErrorSheetModel() =
    default;

std::u16string AuthenticatorCableErrorSheetModel::GetStepTitle() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_GENERIC_TITLE);
}

std::u16string AuthenticatorCableErrorSheetModel::GetStepDescription() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CABLEV2_ERROR_DESCRIPTION);
}

std::u16string AuthenticatorCableErrorSheetModel::GetCancelButtonLabel() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CABLEV2_ERROR_CLOSE);
}

// AuthenticatorChromeProfileCreatePasskeySheetModel
// --------------------------------------------------

AuthenticatorChromeProfileCreatePasskeySheetModel::
    AuthenticatorChromeProfileCreatePasskeySheetModel(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kVisible) {
  lottie_illustrations_.emplace(IDR_WEBAUTHN_PASSKEY_LIGHT,
                                IDR_WEBAUTHN_PASSKEY_DARK);
}

AuthenticatorChromeProfileCreatePasskeySheetModel::
    ~AuthenticatorChromeProfileCreatePasskeySheetModel() = default;

std::u16string AuthenticatorChromeProfileCreatePasskeySheetModel::GetStepTitle()
    const {
  return l10n_util::GetStringFUTF16(
      IDS_WEBAUTHN_CREATE_PASSKEY_TITLE,
      GetRelyingPartyIdStringTitle(dialog_model()));
}

std::u16string
AuthenticatorChromeProfileCreatePasskeySheetModel::GetStepDescription() const {
  return u"";
}

std::u16string
AuthenticatorChromeProfileCreatePasskeySheetModel::passkey_storage_description()
    const {
  return l10n_util::GetStringUTF16(
      dialog_model()->is_off_the_record
          ? IDS_WEBAUTHN_CREATE_PASSKEY_EXTRA_INCOGNITO
          : IDS_WEBAUTHN_CREATE_PASSKEY_EXTRA);
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorChromeProfileCreatePasskeySheetModel::GetAcceptButtonState()
    const {
  return AcceptButtonState::kEnabled;
}

std::u16string
AuthenticatorChromeProfileCreatePasskeySheetModel::GetAcceptButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CONTINUE);
}

void AuthenticatorChromeProfileCreatePasskeySheetModel::OnAccept() {
  dialog_model()->OnChromeProfileCreatePasskeyAccepted();
}

// AuthenticatorGPMErrorSheetModel -------------------------------------------

AuthenticatorGPMErrorSheetModel::AuthenticatorGPMErrorSheetModel(
    AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kVisible) {
  vector_illustrations_.emplace(kPasskeyErrorCustomIcon,
                                kPasskeyErrorDarkCustomIcon);
  if (dialog_model->in_onboarding_flow) {
    RecordOnboardingEvent(webauthn::metrics::OnboardingEvents::kFailure);
  }
  webauthn::user_actions::RecordGpmFailureShown();
  switch (dialog_model->request_type) {
    case device::FidoRequestType::kGetAssertion:
      RecordGPMGetAssertionEvent(
          webauthn::metrics::GPMGetAssertionEvents::kFailure);
      break;
    case device::FidoRequestType::kMakeCredential:
      RecordGPMMakeCredentialEvent(
          webauthn::metrics::GPMMakeCredentialEvents::kFailure);
      break;
  }
}

AuthenticatorGPMErrorSheetModel::~AuthenticatorGPMErrorSheetModel() = default;

std::u16string AuthenticatorGPMErrorSheetModel::GetStepTitle() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_GPM_ERROR_TITLE);
}

std::u16string AuthenticatorGPMErrorSheetModel::GetStepDescription() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_GPM_ERROR_DESC);
}

// AuthenticatorGPMConnectingSheetModel --------------------------------------

AuthenticatorGPMConnectingSheetModel::AuthenticatorGPMConnectingSheetModel(
    AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kHidden) {
  lottie_illustrations_.emplace(IDR_WEBAUTHN_HYBRID_CONNECTING_LIGHT,
                                IDR_WEBAUTHN_HYBRID_CONNECTING_DARK);
}

AuthenticatorGPMConnectingSheetModel::~AuthenticatorGPMConnectingSheetModel() =
    default;

std::u16string AuthenticatorGPMConnectingSheetModel::GetStepTitle() const {
  return u"";
}

std::u16string AuthenticatorGPMConnectingSheetModel::GetStepDescription()
    const {
  return u"";
}

// AuthenticatorMultiSourcePickerSheetModel --------------------------------

AuthenticatorMultiSourcePickerSheetModel::
    AuthenticatorMultiSourcePickerSheetModel(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model) {
  lottie_illustrations_.emplace(IDR_WEBAUTHN_PASSKEY_LIGHT,
                                IDR_WEBAUTHN_PASSKEY_DARK);

  webauthn::user_actions::RecordMultipleOptionsShown(
      dialog_model->mechanisms, dialog_model->request_type);
  if (std::ranges::any_of(dialog_model->mechanisms,
                          &IsLocalPasskeyOrEnclaveAuthenticatorOrPassword)) {
    primary_passkeys_label_ =
        l10n_util::GetStringUTF16(IDS_WEBAUTHN_THIS_DEVICE_LABEL);
    for (size_t i = 0; i < dialog_model->mechanisms.size(); ++i) {
      const AuthenticatorRequestDialogModel::Mechanism& mech =
          dialog_model->mechanisms[i];
      if (IsLocalPasskeyOrEnclaveAuthenticatorOrPassword(mech) ||
          // iCloud Keychain appears in the primary list if present. This
          // happens when Chrome does not have permission to enumerate
          // credentials from iCloud Keychain. Thus this generic option is the
          // only way for the user to trigger it.
          std::holds_alternative<ICloudKeychainMech>(mech.type)) {
        primary_passkey_indices_.push_back(i);
      } else {
        secondary_passkey_indices_.push_back(i);
      }
      if (std::holds_alternative<PasswordMech>(mech.type)) {
        has_passwords_ = true;
      }
    }
    return;
  }

  for (size_t i = 0; i < dialog_model->mechanisms.size(); ++i) {
    secondary_passkey_indices_.push_back(i);
  }
}

AuthenticatorMultiSourcePickerSheetModel::
    ~AuthenticatorMultiSourcePickerSheetModel() = default;

std::u16string AuthenticatorMultiSourcePickerSheetModel::GetStepTitle() const {
  if (has_passwords_) {
    return u"Use a saved credential for " +
           GetRelyingPartyIdStringTitle(dialog_model()) + u" (UT)";
  }
  return l10n_util::GetStringFUTF16(
      IDS_WEBAUTHN_CHOOSE_PASSKEY_FOR_RP_TITLE,
      GetRelyingPartyIdStringTitle(dialog_model()));
}

std::u16string AuthenticatorMultiSourcePickerSheetModel::GetStepDescription()
    const {
  return u"";
}

// AuthenticatorPriorityMechanismSheetModel --------------------------------

AuthenticatorPriorityMechanismSheetModel::
    AuthenticatorPriorityMechanismSheetModel(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kVisible) {
  lottie_illustrations_.emplace(IDR_WEBAUTHN_PASSKEY_LIGHT,
                                IDR_WEBAUTHN_PASSKEY_DARK);

  webauthn::user_actions::RecordPriorityOptionShown(
      dialog_model->mechanisms[*dialog_model->priority_mechanism_index]);
}
AuthenticatorPriorityMechanismSheetModel::
    ~AuthenticatorPriorityMechanismSheetModel() = default;

std::u16string AuthenticatorPriorityMechanismSheetModel::GetStepTitle() const {
  return l10n_util::GetStringFUTF16(
      IDS_WEBAUTHN_USE_PASSKEY_TITLE,
      GetRelyingPartyIdStringTitle(dialog_model()));
}

std::u16string AuthenticatorPriorityMechanismSheetModel::GetStepDescription()
    const {
  return u"";
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorPriorityMechanismSheetModel::GetAcceptButtonState() const {
  return AcceptButtonState::kEnabled;
}

std::u16string AuthenticatorPriorityMechanismSheetModel::GetAcceptButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CONTINUE);
}

void AuthenticatorPriorityMechanismSheetModel::OnAccept() {
  dialog_model()->OnUserConfirmedPriorityMechanism();
}

// AuthenticatorGpmPinSheetModelBase -------------------------------------------

AuthenticatorGpmPinSheetModelBase::AuthenticatorGpmPinSheetModelBase(
    AuthenticatorRequestDialogModel* dialog_model,
    Mode mode)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kHidden),
      mode_(mode) {}

AuthenticatorGpmPinSheetModelBase::~AuthenticatorGpmPinSheetModelBase() =
    default;

std::u16string AuthenticatorGpmPinSheetModelBase::GetGpmAccountEmail() const {
  std::optional<AccountInfo> account_info = dialog_model()->GetGpmAccountInfo();
  if (!account_info) {
    return std::u16string();
  }
  return base::UTF8ToUTF16(account_info->GetEmail());
}

std::u16string AuthenticatorGpmPinSheetModelBase::GetGpmAccountName() const {
  std::optional<AccountInfo> account_info = dialog_model()->GetGpmAccountInfo();
  if (!account_info) {
    return std::u16string();
  }
  return base::UTF8ToUTF16(account_info->GetFullName().value_or(""));
}

gfx::Image AuthenticatorGpmPinSheetModelBase::GetGpmAccountImage() const {
  std::optional<AccountInfo> account_info = dialog_model()->GetGpmAccountInfo();
  if (!account_info) {
    return gfx::Image();
  }
  gfx::Image account_image = account_info->GetAvatarImage().value_or(
      ui::ResourceBundle::GetSharedInstance().GetImageNamed(
          profiles::GetPlaceholderAvatarIconResourceID()));
  constexpr int kAvatarIconSize = 32;
  return profiles::GetSizedAvatarIcon(account_image,
                                      /*width=*/kAvatarIconSize,
                                      /*height=*/kAvatarIconSize,
                                      profiles::SHAPE_CIRCLE);
}

std::u16string AuthenticatorGpmPinSheetModelBase::GetAccessibleDescription()
    const {
  std::u16string error = GetError();
  return error.empty() ? GetHint() : error;
}

bool AuthenticatorGpmPinSheetModelBase::ui_disabled() const {
  return dialog_model()->ui_disabled_;
}

std::u16string AuthenticatorGpmPinSheetModelBase::GetStepTitle() const {
  switch (mode_) {
    case Mode::kPinCreate:
      return l10n_util::GetStringUTF16(IDS_WEBAUTHN_GPM_CREATE_PIN_TITLE);
    case Mode::kPinEntry:
      return l10n_util::GetStringUTF16(IDS_WEBAUTHN_GPM_ENTER_PIN_TITLE);
  }
}

std::u16string AuthenticatorGpmPinSheetModelBase::GetStepDescription() const {
  switch (mode_) {
    case Mode::kPinCreate:
      return l10n_util::GetStringUTF16(IDS_WEBAUTHN_GPM_CREATE_PIN_DESC);
    case Mode::kPinEntry:
      return l10n_util::GetStringFUTF16(
          IDS_WEBAUTHN_GPM_ENTER_PIN_DESC,
          GetRelyingPartyIdStringLabel(dialog_model()));
  }
}

std::u16string AuthenticatorGpmPinSheetModelBase::GetError() const {
  std::optional<int> remaining_attempts =
      dialog_model()->gpm_pin_remaining_attempts_;
  return remaining_attempts && mode_ == Mode::kPinEntry
             ? l10n_util::GetPluralStringFUTF16(
                   IDS_WEBAUTHN_GPM_WRONG_PIN_ERROR, *remaining_attempts)
             : std::u16string();
}

bool AuthenticatorGpmPinSheetModelBase::IsForgotGPMPinButtonVisible() const {
  return mode_ == Mode::kPinEntry;
}

bool AuthenticatorGpmPinSheetModelBase::IsGPMPinOptionsButtonVisible() const {
  return mode_ == Mode::kPinCreate;
}

void AuthenticatorGpmPinSheetModelBase::OnAccept() {
  webauthn::user_actions::RecordAcceptClick();
  dialog_model()->OnGPMPinEntered(pin_);
}

void AuthenticatorGpmPinSheetModelBase::OnCancel() {
  if (dialog_model()->in_onboarding_flow) {
    RecordOnboardingEvent(webauthn::metrics::OnboardingEvents::
                              kAuthenticatorGpmPinSheetCancelled);
  }
  AuthenticatorSheetModelBase::OnCancel();
}

void AuthenticatorGpmPinSheetModelBase::OnForgotGPMPin() const {
  webauthn::user_actions::RecordGpmForgotPinClick();
  dialog_model()->OnGPMForgotPinPressed();
}

void AuthenticatorGpmPinSheetModelBase::OnGPMPinOptionChosen(
    bool is_arbitrary) const {
  if ((dialog_model()->step() == Step::kGPMChangeArbitraryPin ||
       dialog_model()->step() == Step::kGPMCreateArbitraryPin ||
       dialog_model()->step() == Step::kGPMEnterArbitraryPin) &&
      is_arbitrary) {
    // The sheet already facilitates entering arbitrary pin.
    return;
  }
  if ((dialog_model()->step() == Step::kGPMChangePin ||
       dialog_model()->step() == Step::kGPMCreatePin ||
       dialog_model()->step() == Step::kGPMEnterPin) &&
      !is_arbitrary) {
    // The sheet already facilitates entering six digit pin.
    return;
  }
  webauthn::user_actions::RecordGpmPinOptionChangeClick();
  dialog_model()->OnGPMPinOptionChanged(is_arbitrary);
}

// AuthenticatorGpmPinSheetModel -----------------------------------------------

AuthenticatorGpmPinSheetModel::AuthenticatorGpmPinSheetModel(
    AuthenticatorRequestDialogModel* dialog_model,
    int pin_digits_count,
    Mode mode)
    : AuthenticatorGpmPinSheetModelBase(dialog_model, mode),
      pin_digits_count_(pin_digits_count) {
  webauthn::user_actions::RecordGpmPinSheetShown(
      dialog_model->request_type,
      /*is_pin_creation=*/mode == Mode::kPinCreate,
      /*is_arbitrary=*/false);
}

AuthenticatorGpmPinSheetModel::~AuthenticatorGpmPinSheetModel() = default;

void AuthenticatorGpmPinSheetModel::PinCharTyped(bool is_digit) {
  if (mode_ != Mode::kPinCreate || show_digit_hint_ != is_digit) {
    return;
  }

  show_digit_hint_ = !is_digit;
  dialog_model()->OnSheetModelChanged();
}

int AuthenticatorGpmPinSheetModel::pin_digits_count() const {
  return pin_digits_count_;
}

void AuthenticatorGpmPinSheetModel::SetPin(std::u16string pin) {
  bool full_pin_typed_before = FullPinTyped();
  pin_ = std::move(pin);
  bool full_pin_typed = FullPinTyped();

  // When entering an existing PIN, the dialog completes as soon as all the
  // digits have been typed. When creating a new PIN, the user has to hit enter
  // to confirm.
  if (mode_ == Mode::kPinEntry && full_pin_typed) {
    dialog_model()->OnGPMPinEntered(pin_);
  } else if (mode_ == Mode::kPinCreate &&
             full_pin_typed_before != full_pin_typed) {
    dialog_model()->OnButtonsStateChanged();
  }
}

std::u16string AuthenticatorGpmPinSheetModel::GetAccessibleName() const {
  std::u16string pin_digits_typed_str = base::NumberToString16(
      std::min(static_cast<int>(pin_.length()) + 1, pin_digits_count_));
  return l10n_util::GetStringFUTF16(
      IDS_WEBAUTHN_GPM_SIX_DIGIT_PIN_ACCESSIBILITY_LABEL, pin_digits_typed_str);
}

bool AuthenticatorGpmPinSheetModel::FullPinTyped() const {
  return static_cast<int>(pin_.length()) == pin_digits_count_;
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorGpmPinSheetModel::GetAcceptButtonState() const {
  if (mode() == Mode::kPinCreate) {
    return FullPinTyped() && !ui_disabled() ? AcceptButtonState::kEnabled
                                            : AcceptButtonState::kDisabled;
  }
  return AcceptButtonState::kNotVisible;
}

std::u16string AuthenticatorGpmPinSheetModel::GetAcceptButtonLabel() const {
  return l10n_util::GetStringUTF16(IDS_CONFIRM);
}

std::u16string AuthenticatorGpmPinSheetModel::GetHint() const {
  return mode_ == Mode::kPinCreate && show_digit_hint_
             ? l10n_util::GetStringUTF16(IDS_WEBAUTHN_GPM_PIN_DIGIT_HINT)
             : std::u16string();
}

// AuthenticatorGpmArbitraryPinSheetModel --------------------------------------

AuthenticatorGpmArbitraryPinSheetModel::AuthenticatorGpmArbitraryPinSheetModel(
    AuthenticatorRequestDialogModel* dialog_model,
    Mode mode)
    : AuthenticatorGpmPinSheetModelBase(dialog_model, mode) {
  webauthn::user_actions::RecordGpmPinSheetShown(
      dialog_model->request_type,
      /*is_pin_creation=*/mode == Mode::kPinCreate,
      /*is_arbitrary=*/true);
}

AuthenticatorGpmArbitraryPinSheetModel::
    ~AuthenticatorGpmArbitraryPinSheetModel() = default;

void AuthenticatorGpmArbitraryPinSheetModel::SetPin(std::u16string pin) {
  bool accept_button_enabled =
      GetAcceptButtonState() == AcceptButtonState::kEnabled;
  pin_ = std::move(pin);
  if (accept_button_enabled !=
      (GetAcceptButtonState() == AcceptButtonState::kEnabled)) {
    dialog_model()->OnButtonsStateChanged();
  }
}

std::u16string AuthenticatorGpmArbitraryPinSheetModel::GetAccessibleName()
    const {
  switch (mode_) {
    case Mode::kPinCreate:
      return l10n_util::GetStringUTF16(
          IDS_WEBAUTHN_GPM_CREATE_ALPHANUMERIC_PIN_ACCESSIBILITY);
    case Mode::kPinEntry:
      return l10n_util::GetStringFUTF16(
          IDS_WEBAUTHN_GPM_ENTER_ALPHANUMERIC_PIN_ACCESSIBILITY_WITH_WEBSITE,
          base::UTF8ToUTF16(dialog_model()->relying_party_id));
  }
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorGpmArbitraryPinSheetModel::GetAcceptButtonState() const {
  return pin_.length() >= kGpmArbitraryPinMinLength && !ui_disabled()
             ? AcceptButtonState::kEnabled
             : AcceptButtonState::kDisabled;
}

std::u16string AuthenticatorGpmArbitraryPinSheetModel::GetAcceptButtonLabel()
    const {
  return mode_ == Mode::kPinEntry
             ? l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_NEXT)
             : l10n_util::GetStringUTF16(IDS_CONFIRM);
}

std::u16string AuthenticatorGpmArbitraryPinSheetModel::GetHint() const {
  return mode_ == Mode::kPinCreate
             ? l10n_util::GetStringUTF16(IDS_WEBAUTHN_GPM_PIN_LENGTH_HINT)
             : std::u16string();
}

// AuthenticatorTrustThisComputerAssertionSheetModel -------------------------

AuthenticatorTrustThisComputerAssertionSheetModel::
    AuthenticatorTrustThisComputerAssertionSheetModel(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kHidden) {
  lottie_illustrations_.emplace(IDR_WEBAUTHN_LAPTOP_LIGHT,
                                IDR_WEBAUTHN_LAPTOP_DARK);

  webauthn::user_actions::RecordTrustDialogShown(dialog_model->request_type);
}

AuthenticatorTrustThisComputerAssertionSheetModel::
    ~AuthenticatorTrustThisComputerAssertionSheetModel() = default;

std::u16string AuthenticatorTrustThisComputerAssertionSheetModel::GetStepTitle()
    const {
  return l10n_util::GetStringUTF16(
      IDS_WEBAUTHN_GPM_TRUST_THIS_COMPUTER_ASSERTION_TITLE);
}

std::u16string
AuthenticatorTrustThisComputerAssertionSheetModel::GetStepDescription() const {
  return l10n_util::GetStringUTF16(
      IDS_WEBAUTHN_GPM_TRUST_THIS_COMPUTER_ASSERTION_DESC);
}

bool AuthenticatorTrustThisComputerAssertionSheetModel::IsCancelButtonVisible()
    const {
  return true;
}

std::u16string
AuthenticatorTrustThisComputerAssertionSheetModel::GetCancelButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_CANCEL);
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorTrustThisComputerAssertionSheetModel::GetAcceptButtonState()
    const {
  return AcceptButtonState::kEnabled;
}

std::u16string
AuthenticatorTrustThisComputerAssertionSheetModel::GetAcceptButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_NEXT);
}

bool AuthenticatorTrustThisComputerAssertionSheetModel::
    IsOtherMechanismButtonVisible() const {
  return true;
}

std::u16string AuthenticatorTrustThisComputerAssertionSheetModel::
    GetOtherMechanismButtonLabel() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_USE_A_DIFFERENT_DEVICE);
}

void AuthenticatorTrustThisComputerAssertionSheetModel::OnAccept() {
  webauthn::user_actions::RecordAcceptClick();
  dialog_model()->OnGPMTrustThisComputer();
}

// AuthenticatorCreateGpmPasskeySheetModel -------------------------------------

AuthenticatorCreateGpmPasskeySheetModel::
    AuthenticatorCreateGpmPasskeySheetModel(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kVisible) {
  lottie_illustrations_.emplace(IDR_WEBAUTHN_GPM_PASSKEY_LIGHT,
                                IDR_WEBAUTHN_GPM_PASSKEY_DARK);

  webauthn::user_actions::RecordCreateGpmDialogShown();
}

AuthenticatorCreateGpmPasskeySheetModel::
    ~AuthenticatorCreateGpmPasskeySheetModel() = default;

std::u16string AuthenticatorCreateGpmPasskeySheetModel::GetStepTitle() const {
  return l10n_util::GetStringFUTF16(
      IDS_WEBAUTHN_GPM_CREATE_PASSKEY_TITLE,
      GetRelyingPartyIdStringTitle(dialog_model()));
}

std::u16string AuthenticatorCreateGpmPasskeySheetModel::GetStepDescription()
    const {
  return l10n_util::GetStringFUTF16(
      IDS_WEBAUTHN_GPM_CREATE_PASSKEY_DESC,
      base::UTF8ToUTF16(dialog_model()->GetGpmAccountEmail()));
}

bool AuthenticatorCreateGpmPasskeySheetModel::IsCancelButtonVisible() const {
  return true;
}

std::u16string AuthenticatorCreateGpmPasskeySheetModel::GetCancelButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_CANCEL);
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorCreateGpmPasskeySheetModel::GetAcceptButtonState() const {
  return AcceptButtonState::kEnabled;
}

std::u16string AuthenticatorCreateGpmPasskeySheetModel::GetAcceptButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CREATE);
}

void AuthenticatorCreateGpmPasskeySheetModel::OnAccept() {
  webauthn::user_actions::RecordAcceptClick();
  dialog_model()->OnGPMCreationConfirmed();
}

void AuthenticatorCreateGpmPasskeySheetModel::OnCancel() {
  if (dialog_model()->in_onboarding_flow) {
    RecordOnboardingEvent(
        webauthn::metrics::OnboardingEvents::kCreateGpmPasskeySheetCancelled);
  }
  AuthenticatorSheetModelBase::OnCancel();
}

void AuthenticatorCreateGpmPasskeySheetModel::OnBack() {
  if (dialog_model()->in_onboarding_flow) {
    RecordOnboardingEvent(webauthn::metrics::OnboardingEvents::
                              kCreateGpmPasskeySheetSaveAnotherWaySelected);
  }
  AuthenticatorSheetModelBase::OnBack();
}

// AuthenticatorTrustThisComputerCreationSheetModel ---------------------

AuthenticatorTrustThisComputerCreationSheetModel::
    AuthenticatorTrustThisComputerCreationSheetModel(
        AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kVisible) {
  lottie_illustrations_.emplace(IDR_WEBAUTHN_LAPTOP_LIGHT,
                                IDR_WEBAUTHN_LAPTOP_DARK);

  webauthn::user_actions::RecordTrustDialogShown(dialog_model->request_type);
}

AuthenticatorTrustThisComputerCreationSheetModel::
    ~AuthenticatorTrustThisComputerCreationSheetModel() = default;

std::u16string AuthenticatorTrustThisComputerCreationSheetModel::GetStepTitle()
    const {
  return l10n_util::GetStringUTF16(
      IDS_WEBAUTHN_GPM_TRUST_THIS_COMPUTER_CREATION_TITLE);
}

std::u16string
AuthenticatorTrustThisComputerCreationSheetModel::GetStepDescription() const {
  return l10n_util::GetStringFUTF16(
      IDS_WEBAUTHN_GPM_TRUST_THIS_COMPUTER_CREATION_DESC,
      base::UTF8ToUTF16(dialog_model()->GetGpmAccountEmail()));
}

bool AuthenticatorTrustThisComputerCreationSheetModel::IsCancelButtonVisible()
    const {
  return true;
}

std::u16string
AuthenticatorTrustThisComputerCreationSheetModel::GetCancelButtonLabel() const {
  return l10n_util::GetStringUTF16(IDS_CANCEL);
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorTrustThisComputerCreationSheetModel::GetAcceptButtonState() const {
  return AcceptButtonState::kEnabled;
}

std::u16string
AuthenticatorTrustThisComputerCreationSheetModel::GetAcceptButtonLabel() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_NEXT);
}

std::u16string
AuthenticatorTrustThisComputerCreationSheetModel::GetOtherMechanismButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_SAVE_ANOTHER_WAY);
}

void AuthenticatorTrustThisComputerCreationSheetModel::OnAccept() {
  webauthn::user_actions::RecordAcceptClick();
  dialog_model()->OnGPMTrustThisComputer();
}

// AuthenticatorGPMLockedPinSheetModel ----------------------------------

AuthenticatorGPMLockedPinSheetModel::AuthenticatorGPMLockedPinSheetModel(
    AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kHidden) {
  lottie_illustrations_.emplace(IDR_WEBAUTHN_GPM_PIN_LOCKED_LIGHT,
                                IDR_WEBAUTHN_GPM_PIN_LOCKED_DARK);
  webauthn::user_actions::RecordGpmLockedShown();
}

AuthenticatorGPMLockedPinSheetModel::~AuthenticatorGPMLockedPinSheetModel() =
    default;

std::u16string AuthenticatorGPMLockedPinSheetModel::GetStepTitle() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_LOCKED_GPM_PIN_TITLE);
}

std::u16string AuthenticatorGPMLockedPinSheetModel::GetStepDescription() const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_LOCKED_GPM_PIN_DESCRIPTION);
}

AuthenticatorRequestSheetModel::AcceptButtonState
AuthenticatorGPMLockedPinSheetModel::GetAcceptButtonState() const {
  return AcceptButtonState::kEnabled;
}

std::u16string AuthenticatorGPMLockedPinSheetModel::GetAcceptButtonLabel()
    const {
  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CHANGE_PIN);
}

void AuthenticatorGPMLockedPinSheetModel::OnAccept() {
  webauthn::user_actions::RecordAcceptClick();
  dialog_model()->OnGPMForgotPinPressed();
}

// CombinedSelectorSheetModel

CombinedSelectorSheetModel::CombinedSelectorSheetModel(
    AuthenticatorRequestDialogModel* dialog_model)
    : AuthenticatorSheetModelBase(dialog_model,
                                  OtherMechanismButtonVisibility::kHidden) {
  webauthn::user_actions::RecordCombinedSelectorShown();
  webauthn::metrics::RecordCombinedSelectorShown(
      /*credential_count=*/dialog_model->mechanisms.size());
}

CombinedSelectorSheetModel::SelectionStatus
CombinedSelectorSheetModel::GetSelectionStatus(size_t index) const {
  if (dialog_model()->mechanisms.size() == 1) {
    return SelectionStatus::kNone;
  }
  return selection_index_ == index ? SelectionStatus::kSelected
                                   : SelectionStatus::kNotSelected;
}

void CombinedSelectorSheetModel::SetSelectionIndex(size_t index) {
  selection_index_ = index;
}

std::u16string CombinedSelectorSheetModel::GetStepTitle() const {
  return l10n_util::GetStringFUTF16(
      IDS_WEBAUTHN_SIGN_IN_TO_WEBSITE_DIALOG_TITLE,
      GetRelyingPartyIdStringTitle(dialog_model()));
}

std::u16string CombinedSelectorSheetModel::GetStepDescription() const {
  return u"";
}

AuthenticatorRequestSheetModel::AcceptButtonState
CombinedSelectorSheetModel::GetAcceptButtonState() const {
  return dialog_model()->ui_disabled_ ? AcceptButtonState::kDisabledWithSpinner
                                      : AcceptButtonState::kEnabled;
}

bool CombinedSelectorSheetModel::IsCancelButtonVisible() const {
  return true;
}

bool CombinedSelectorSheetModel::IsActivityIndicatorVisible() const {
  return false;
}

std::u16string CombinedSelectorSheetModel::GetCancelButtonLabel() const {
  return l10n_util::GetStringUTF16(IDS_SIGNIN_ACCESSIBLE_CLOSE_BUTTON);
}

std::u16string CombinedSelectorSheetModel::GetAcceptButtonLabel() const {
  return l10n_util::GetStringUTF16(
      IDS_PASSWORD_MANAGER_ACCOUNT_CHOOSER_SIGN_IN);
}

void CombinedSelectorSheetModel::OnAccept() {
  webauthn::metrics::RecordCombinedSelectorAccept(
      /*credential_count=*/dialog_model()->mechanisms.size(),
      /*default_selected=*/selection_index_ == 0);
  const auto& mech = dialog_model()->mechanisms.at(selection_index_);
  webauthn::user_actions::RecordMechanismClick(mech);
  mech.callback.Run();
}

void CombinedSelectorSheetModel::OnCancel() {
  webauthn::metrics::RecordCombinedSelectorCancelButtonClicked();
  AuthenticatorSheetModelBase::OnCancel();
}
