// Copyright 2015 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/extensions/api/passwords_private/passwords_private_delegate_impl.h"

#include <memory>
#include <optional>
#include <string>
#include <utility>

#include "ash/constants/web_app_id_constants.h"
#include "base/check.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/i18n/icubridge/date_time_formatter.h"
#include "base/i18n/icubridge/icu_bridge.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/notimplemented.h"
#include "base/notreached.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h"
#include "chrome/browser/password_manager/chrome_password_change_service.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
#include "chrome/browser/ui/hats/trust_safety_sentiment_service.h"
#include "chrome/browser/ui/navigator/browser_navigator.h"
#include "chrome/browser/ui/navigator/browser_navigator_params.h"
#include "chrome/browser/ui/user_education/browser_user_education_interface.h"
#include "chrome/browser/ui/web_applications/web_app_dialog_utils.h"
#include "chrome/browser/web_applications/web_app_install_manager.h"
#include "chrome/browser/web_applications/web_app_install_params.h"
#include "chrome/browser/webauthn/change_pin_controller.h"
#include "chrome/browser/webauthn/enclave_manager.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/extensions/api/passwords_private.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/constants/chromeos_features.h"
#include "components/affiliations/core/browser/affiliation_utils.h"
#include "components/feature_engagement/public/feature_constants.h"
#include "components/keyed_service/core/service_access_type.h"
#include "components/password_manager/core/browser/features/password_features.h"
#include "components/password_manager/core/browser/features/password_manager_features_util.h"
#include "components/password_manager/core/browser/form_parsing/form_data_parser.h"
#include "components/password_manager/core/browser/leak_detection/leak_detection_request_utils.h"
#include "components/password_manager/core/browser/password_form.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/browser/password_store/password_form_converters.h"
#include "components/password_manager/core/browser/password_store/password_store_util.h"
#include "components/password_manager/core/browser/password_sync_util.h"
#include "components/password_manager/core/browser/sharing/password_sender_service.h"
#include "components/password_manager/core/browser/sharing/recipients_fetcher_impl.h"
#include "components/password_manager/core/browser/ui/credential_ui_entry.h"
#include "components/password_manager/core/browser/ui/credential_utils.h"
#include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
#include "components/password_manager/core/common/password_manager_constants.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/base/signin_metrics.h"
#include "components/sync/base/user_selectable_type.h"
#include "components/sync/service/sync_service.h"
#include "components/sync/service/sync_user_settings.h"
#include "components/url_formatter/elide_url.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/clipboard_sequence_number_token.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/l10n/time_format.h"
#include "url/gurl.h"
#include "url/scheme_host_port.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/extensions/api/passwords_private/passwords_private_utils_chromeos.h"
#endif

namespace {

using password_manager::CredentialFacet;
using password_manager::CredentialUIEntry;
using password_manager::FetchFamilyMembersRequestStatus;
using password_manager::constants::kPasswordManagerAuthValidity;
// Time to keep password in clipboard before clearing.
constexpr base::TimeDelta kClipboardClearDelay = base::Seconds(120);

// Map password_manager::ExportProgressStatus to
// extensions::api::passwords_private::ExportProgressStatus.
extensions::api::passwords_private::ExportProgressStatus ConvertStatus(
    password_manager::ExportProgressStatus status) {
  switch (status) {
    case password_manager::ExportProgressStatus::kNotStarted:
      return extensions::api::passwords_private::ExportProgressStatus::
          kNotStarted;
    case password_manager::ExportProgressStatus::kInProgress:
      return extensions::api::passwords_private::ExportProgressStatus::
          kInProgress;
    case password_manager::ExportProgressStatus::kSucceeded:
      return extensions::api::passwords_private::ExportProgressStatus::
          kSucceeded;
    case password_manager::ExportProgressStatus::kFailedCancelled:
      return extensions::api::passwords_private::ExportProgressStatus::
          kFailedCancelled;
    case password_manager::ExportProgressStatus::kFailedWrite:
      return extensions::api::passwords_private::ExportProgressStatus::
          kFailedWriteFailed;
  }

  NOTREACHED();
}

std::u16string GetReauthPurpose(
    extensions::api::passwords_private::PlaintextReason reason) {
#if BUILDFLAG(IS_MAC)

  switch (reason) {
    case extensions::api::passwords_private::PlaintextReason::kView:
      return l10n_util::GetStringUTF16(
          IDS_PASSWORDS_PAGE_AUTHENTICATION_PROMPT_BIOMETRIC_SUFFIX);
    case extensions::api::passwords_private::PlaintextReason::kCopy:
      return l10n_util::GetStringUTF16(
          IDS_PASSWORDS_PAGE_COPY_AUTHENTICATION_PROMPT_BIOMETRIC_SUFFIX);
    case extensions::api::passwords_private::PlaintextReason::kEdit:
      return l10n_util::GetStringUTF16(
          IDS_PASSWORDS_PAGE_EDIT_AUTHENTICATION_PROMPT_BIOMETRIC_SUFFIX);
    case extensions::api::passwords_private::PlaintextReason::kNone:
      NOTREACHED();
  }
#elif BUILDFLAG(IS_WIN)
  switch (reason) {
    case extensions::api::passwords_private::PlaintextReason::kView:
      return l10n_util::GetStringUTF16(
          IDS_PASSWORDS_PAGE_AUTHENTICATION_PROMPT);
    case extensions::api::passwords_private::PlaintextReason::kCopy:
      return l10n_util::GetStringUTF16(
          IDS_PASSWORDS_PAGE_COPY_AUTHENTICATION_PROMPT);
    case extensions::api::passwords_private::PlaintextReason::kEdit:
      return l10n_util::GetStringUTF16(
          IDS_PASSWORDS_PAGE_EDIT_AUTHENTICATION_PROMPT);
    case extensions::api::passwords_private::PlaintextReason::kNone:
      NOTREACHED();
  }
#elif BUILDFLAG(IS_CHROMEOS)
  switch (reason) {
    case extensions::api::passwords_private::PlaintextReason::kView:
      return l10n_util::GetStringUTF16(
          IDS_PASSWORDS_PAGE_AUTHENTICATION_PROMPT_CHROMEOS);
    case extensions::api::passwords_private::PlaintextReason::kCopy:
    case extensions::api::passwords_private::PlaintextReason::kEdit:
      return std::u16string();
    case extensions::api::passwords_private::PlaintextReason::kNone:
      NOTREACHED();
  }
#else
  return std::u16string();
#endif
}

password_manager::metrics_util::AccessPasswordInSettingsEvent
ConvertPlaintextReason(
    extensions::api::passwords_private::PlaintextReason reason) {
  switch (reason) {
    case extensions::api::passwords_private::PlaintextReason::kCopy:
      return password_manager::metrics_util::ACCESS_PASSWORD_COPIED;
    case extensions::api::passwords_private::PlaintextReason::kView:
      return password_manager::metrics_util::ACCESS_PASSWORD_VIEWED;
    case extensions::api::passwords_private::PlaintextReason::kEdit:
      return password_manager::metrics_util::ACCESS_PASSWORD_EDITED;
    case extensions::api::passwords_private::PlaintextReason::kNone:
      NOTREACHED();
  }
}

base::flat_set<password_manager::PasswordForm::Store>
ConvertToPasswordFormStores(
    extensions::api::passwords_private::PasswordStoreSet store) {
  switch (store) {
    case extensions::api::passwords_private::PasswordStoreSet::
        kDeviceAndAccount:
      return {password_manager::PasswordForm::Store::kProfileStore,
              password_manager::PasswordForm::Store::kAccountStore};
    case extensions::api::passwords_private::PasswordStoreSet::kDevice:
      return {password_manager::PasswordForm::Store::kProfileStore};
    case extensions::api::passwords_private::PasswordStoreSet::kAccount:
      return {password_manager::PasswordForm::Store::kAccountStore};
    default:
      break;
  }
  NOTREACHED();
}

extensions::api::passwords_private::ImportEntry ConvertImportEntry(
    const password_manager::ImportEntry& entry) {
  extensions::api::passwords_private::ImportEntry result;
  result.status =
      static_cast<extensions::api::passwords_private::ImportEntryStatus>(
          entry.status);
  result.url = entry.url;
  result.username = entry.username;
  result.password = entry.password;
  result.id = entry.id;
  return result;
}

// Maps password_manager::ImportResults to
// extensions::api::passwords_private::ImportResults.
extensions::api::passwords_private::ImportResults ConvertImportResults(
    const password_manager::ImportResults& results) {
  base::UmaHistogramEnumeration("PasswordManager.ImportResultsStatus2",
                                results.status);
  extensions::api::passwords_private::ImportResults private_results;
  private_results.status =
      static_cast<extensions::api::passwords_private::ImportResultsStatus>(
          results.status);
  private_results.number_imported = results.number_imported;
  private_results.file_name = results.file_name;
  private_results.displayed_entries.reserve(results.displayed_entries.size());
  for (const auto& entry : results.displayed_entries) {
    private_results.displayed_entries.emplace_back(ConvertImportEntry(entry));
  }
  return private_results;
}

#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)

using password_manager::prefs::kBiometricAuthenticationBeforeFilling;

void ChangeBiometricAuthenticationBeforeFillingSetting(
    PrefService* prefs,
    extensions::PasswordsPrivateDelegate::AuthenticationCallback callback,
    bool success) {
  if (success) {
    prefs->SetBoolean(
        kBiometricAuthenticationBeforeFilling,
        !prefs->GetBoolean(kBiometricAuthenticationBeforeFilling));
  }

  std::move(callback).Run(success);
}

std::u16string GetMessageForBiometricAuthenticationBeforeFillingSetting(
    PrefService* prefs) {
  std::u16string message;
#if BUILDFLAG(IS_MAC)
  const bool pref_enabled =
      prefs->GetBoolean(kBiometricAuthenticationBeforeFilling);
  message = l10n_util::GetStringUTF16(
      pref_enabled ? IDS_PASSWORD_MANAGER_TURN_OFF_FILLING_REAUTH_MAC
                   : IDS_PASSWORD_MANAGER_TURN_ON_FILLING_REAUTH_MAC);
#elif BUILDFLAG(IS_WIN)
  const bool pref_enabled =
      prefs->GetBoolean(kBiometricAuthenticationBeforeFilling);
  message = l10n_util::GetStringUTF16(
      pref_enabled ? IDS_PASSWORD_MANAGER_TURN_OFF_FILLING_REAUTH_WIN
                   : IDS_PASSWORD_MANAGER_TURN_ON_FILLING_REAUTH_WIN);
#elif BUILDFLAG(IS_CHROMEOS)
  const bool pref_enabled =
      prefs->GetBoolean(kBiometricAuthenticationBeforeFilling);
  message = l10n_util::GetStringUTF16(
      pref_enabled ? IDS_PASSWORD_MANAGER_TURN_OFF_FILLING_REAUTH_CHROMEOS
                   : IDS_PASSWORD_MANAGER_TURN_ON_FILLING_REAUTH_CHROMEOS);
#endif
  return message;
}

#endif



extensions::api::passwords_private::PasswordManagerActionableError
ToActionableApiError(password_manager::ActionableError error) {
  using extensions::api::passwords_private::PasswordManagerActionableError;
  switch (error) {
    case password_manager::ActionableError::kNoError:
      return PasswordManagerActionableError::kNoError;
    case password_manager::ActionableError::kInactionable:
      return PasswordManagerActionableError::kInactionable;
    case password_manager::ActionableError::kInactionableTemporaryError:
      return PasswordManagerActionableError::kInactionableTemporaryError;
    case password_manager::ActionableError::kSignInNeeded:
      return PasswordManagerActionableError::kSignInNeeded;
    case password_manager::ActionableError::kKeychainError:
      return PasswordManagerActionableError::kKeychainError;
    case password_manager::ActionableError::kTrustedVaultKeyNeeded:
      return PasswordManagerActionableError::kTrustedVaultKeyNeeded;
    case password_manager::ActionableError::kNeedsPassphrase:
      return PasswordManagerActionableError::kNeedsPassphrase;
  }
}

}  // namespace

namespace extensions {

PasswordsPrivateDelegateImpl::PasswordsPrivateDelegateImpl(
    PrefService* prefs,
    signin::IdentityManager* identity_manager,
    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
    password_manager::PasswordSenderService* password_sender_service,
    syncer::SyncService* sync_service,
    TrustSafetySentimentService* trust_safety_sentiment_service,
    affiliations::AffiliationService* affiliation_service,
    scoped_refptr<password_manager::PasswordStoreInterface>
        profile_password_store,
    scoped_refptr<password_manager::PasswordStoreInterface>
        account_password_store,
    webauthn::PasskeyModel* passkey_model,
    password_manager::BulkLeakCheckServiceInterface* bulk_leak_check_service,
    PasswordsPrivateEventRouter* event_router,
    web_app::WebAppInstallManager* web_app_install_manager,
    EnclaveManagerInterface* enclave_manager,
    const DeviceAuthenticatorFactory& device_authenticator_factory,
    const base::RepeatingClosure& maybe_show_profile_switch_iph_cb)
    : prefs_(prefs),
      identity_manager_(identity_manager),
      url_loader_factory_(std::move(url_loader_factory)),
      password_sender_service_(password_sender_service),
      sync_service_(sync_service),
      trust_safety_sentiment_service_(trust_safety_sentiment_service),
      profile_password_store_(profile_password_store),
      account_password_store_(account_password_store),
      event_router_(event_router),
      enclave_manager_(enclave_manager),
      device_authenticator_factory_(device_authenticator_factory),
      maybe_show_profile_switch_iph_cb_(maybe_show_profile_switch_iph_cb),
      saved_passwords_presenter_(affiliation_service,
                                 profile_password_store,
                                 account_password_store,
                                 passkey_model),
      password_import_controller_(std::make_unique<PasswordImportController>(
          saved_passwords_presenter_)),
      password_export_controller_(std::make_unique<PasswordExportController>(
          saved_passwords_presenter_,
          base::BindRepeating(
              &PasswordsPrivateDelegateImpl::OnPasswordsExportProgress,
              base::Unretained(this)))),
      password_check_delegate_(prefs_,
                               bulk_leak_check_service,
                               &saved_passwords_presenter_,
                               &credential_id_generator_,
                               event_router_),
      current_entries_initialized_(false) {
  auth_timeout_handler_.Init(
      base::BindRepeating(&PasswordsPrivateDelegateImpl::OsReauthTimeoutCall,
                          weak_ptr_factory_.GetWeakPtr()));
  saved_passwords_presenter_.AddObserver(this);
  saved_passwords_presenter_.Init();

  if (profile_password_store_) {
    profile_password_store_observation_.Observe(profile_password_store_.get());
  }
  if (account_password_store_) {
    account_password_store_observation_.Observe(account_password_store_.get());
  }

  if (sync_service_) {
    sync_service_observation_.Observe(sync_service_);
  }

#if !BUILDFLAG(IS_CHROMEOS)
  install_manager_observation_.Observe(web_app_install_manager);
#endif
}

PasswordsPrivateDelegateImpl::~PasswordsPrivateDelegateImpl() {
  saved_passwords_presenter_.RemoveObserver(this);
  install_manager_observation_.Reset();
#if !BUILDFLAG(IS_WIN)
  if (device_authenticator_) {
    device_authenticator_->Cancel();
  }
#endif
  device_authenticator_.reset();
}

#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
std::unique_ptr<device_reauth::DeviceAuthenticator>
PasswordsPrivateDelegateImpl::GetDeviceAuthenticator(
    base::TimeDelta auth_validity_period) {
  if (test_device_authenticator_) {
    return std::move(test_device_authenticator_);
  }

  device_reauth::DeviceAuthParams params(
      auth_validity_period, device_reauth::DeviceAuthSource::kPasswordManager,
      "PasswordManager.ReauthToAccessPasswordInSettings");

  return device_authenticator_factory_.Run(params);
}
#endif

void PasswordsPrivateDelegateImpl::AddObserver(
    PasswordsPrivateDelegate::Observer* observer) {
  observers_.AddObserver(observer);
}

void PasswordsPrivateDelegateImpl::RemoveObserver(
    PasswordsPrivateDelegate::Observer* observer) {
  observers_.RemoveObserver(observer);
}

password_manager::SavedPasswordsPresenter*
PasswordsPrivateDelegateImpl::GetSavedPasswordsPresenter() {
  return &saved_passwords_presenter_;
}

void PasswordsPrivateDelegateImpl::GetSavedPasswordsList(
    UiEntriesCallback callback) {
  if (current_entries_initialized_) {
    std::move(callback).Run(current_entries_);
  } else {
    get_saved_passwords_list_callbacks_.push_back(std::move(callback));
  }
}

PasswordsPrivateDelegate::CredentialsGroups
PasswordsPrivateDelegateImpl::GetCredentialGroups() {
  std::vector<api::passwords_private::CredentialGroup> groups;
  for (const password_manager::AffiliatedGroup& group :
       saved_passwords_presenter_.GetAffiliatedGroups()) {
    api::passwords_private::CredentialGroup group_api;
    group_api.name = group.GetDisplayName();
    group_api.icon_url = group.GetAllowedIconUrl(sync_service_).spec();

    CHECK(!group.GetCredentials().empty());
    for (const CredentialUIEntry& credential : group.GetCredentials()) {
      group_api.entries.push_back(
          CreatePasswordUiEntryFromCredentialUiEntry(credential));
    }

    groups.push_back(std::move(group_api));
  }
  return groups;
}

void PasswordsPrivateDelegateImpl::GetPasswordExceptionsList(
    ExceptionEntriesCallback callback) {
  if (current_entries_initialized_) {
    std::move(callback).Run(current_exceptions_);
  } else {
    get_password_exception_list_callbacks_.push_back(std::move(callback));
  }
}

std::optional<api::passwords_private::UrlCollection>
PasswordsPrivateDelegateImpl::GetUrlCollection(const std::string& url) {
  GURL url_with_scheme = password_manager_util::ConstructGURLWithScheme(url);
  if (!password_manager::IsValidPasswordURL(url_with_scheme)) {
    return std::nullopt;
  }
  return std::optional<api::passwords_private::UrlCollection>(
      CreateUrlCollectionFromGURL(
          password_manager_util::StripAuthAndParams(url_with_scheme)));
}

bool PasswordsPrivateDelegateImpl::AddPassword(const std::string& url,
                                               const std::u16string& username,
                                               const std::u16string& password,
                                               const std::u16string& note,
                                               bool use_account_store) {
  password_manager::PasswordForm::Store store_to_use =
      use_account_store ? password_manager::PasswordForm::Store::kAccountStore
                        : password_manager::PasswordForm::Store::kProfileStore;
  CredentialUIEntry credential;

  CredentialFacet facet;
  facet.url = password_manager_util::StripAuthAndParams(
      password_manager_util::ConstructGURLWithScheme(url));
  facet.signon_realm = password_manager::GetSignonRealm(facet.url);
  credential.facets.push_back(std::move(facet));
  credential.username = username;
  credential.password = password;
  credential.note = note;
  credential.stored_in = {store_to_use};
  return saved_passwords_presenter_.AddCredential(credential);
}

bool PasswordsPrivateDelegateImpl::ChangeCredential(
    const api::passwords_private::PasswordUiEntry& credential) {
  const CredentialUIEntry* original_credential =
      credential_id_generator_.TryGetKey(credential.id);
  if (!original_credential) {
    return false;
  }
  CredentialUIEntry updated_credential = *original_credential;
  updated_credential.username = base::UTF8ToUTF16(credential.username);
  if (credential.password) {
    updated_credential.password = base::UTF8ToUTF16(*credential.password);
  }
  if (credential.note) {
    updated_credential.note = base::UTF8ToUTF16(*credential.note);
  }
  if (credential.display_name) {
    CHECK(!updated_credential.passkey_credential_id.empty());
    updated_credential.user_display_name =
        base::UTF8ToUTF16(*credential.display_name);
  }
  switch (saved_passwords_presenter_.EditSavedCredentials(*original_credential,
                                                          updated_credential)) {
    case password_manager::SavedPasswordsPresenter::EditResult::kSuccess:
    case password_manager::SavedPasswordsPresenter::EditResult::kNothingChanged:
      return true;
    case password_manager::SavedPasswordsPresenter::EditResult::kNotFound:
    case password_manager::SavedPasswordsPresenter::EditResult::kAlreadyExisits:
    case password_manager::SavedPasswordsPresenter::EditResult::kEmptyPassword:
      return false;
  }
}

void PasswordsPrivateDelegateImpl::RemoveCredential(
    int id,
    api::passwords_private::PasswordStoreSet from_stores) {
  const CredentialUIEntry* entry = credential_id_generator_.TryGetKey(id);
  if (!entry) {
    return;
  }

  CredentialUIEntry copy = *entry;
  copy.stored_in = ConvertToPasswordFormStores(from_stores);

  saved_passwords_presenter_.RemoveCredential(copy);

  // Record that a password removal action happened.
  if (copy.stored_in.contains(
          password_manager::PasswordForm::Store::kAccountStore)) {
    AddPasswordRemovalReason(
        prefs_, password_manager::IsAccountStore(true),
        password_manager::metrics_util::PasswordManagerCredentialRemovalReason::
            kSettings);
  }
  if (copy.stored_in.contains(
          password_manager::PasswordForm::Store::kProfileStore)) {
    AddPasswordRemovalReason(
        prefs_, password_manager::IsAccountStore(false),
        password_manager::metrics_util::PasswordManagerCredentialRemovalReason::
            kSettings);
  }

  if (entry->blocked_by_user) {
    base::RecordAction(
        base::UserMetricsAction("PasswordManager_RemovePasswordException"));
  } else if (!entry->passkey_credential_id.empty()) {
    base::RecordAction(
        base::UserMetricsAction("PasswordManager_RemovePasskey"));
  } else {
    base::RecordAction(
        base::UserMetricsAction("PasswordManager_RemoveSavedPassword"));
  }
}

void PasswordsPrivateDelegateImpl::RemoveBackupPassword(int id) {
  const CredentialUIEntry* entry = credential_id_generator_.TryGetKey(id);
  if (!entry || !entry->backup_password) {
    return;
  }

  saved_passwords_presenter_.RemoveBackupPassword(*entry);
}

void PasswordsPrivateDelegateImpl::RemovePasswordException(int id) {
  RemoveCredential(id,
                   api::passwords_private::PasswordStoreSet::kDeviceAndAccount);
}

void PasswordsPrivateDelegateImpl::UndoRemoveSavedPasswordOrException() {
  saved_passwords_presenter_.UndoLastRemoval();
}

void PasswordsPrivateDelegateImpl::RequestPlaintextPassword(
    int id,
    api::passwords_private::PlaintextReason reason,
    PlaintextPasswordCallback callback) {
  AuthenticateUser(
      kPasswordManagerAuthValidity, GetReauthPurpose(reason),
      base::BindOnce(
          &PasswordsPrivateDelegateImpl::OnRequestPlaintextPasswordAuthResult,
          weak_ptr_factory_.GetWeakPtr(), id, reason, std::move(callback)));
}

void PasswordsPrivateDelegateImpl::RequestCredentialsDetails(
    const std::vector<int>& ids,
    UiEntriesCallback callback,
    content::WebContents* web_contents) {
  AuthenticateUser(
      kPasswordManagerAuthValidity,
      GetReauthPurpose(api::passwords_private::PlaintextReason::kView),
      base::BindOnce(
          &PasswordsPrivateDelegateImpl::OnRequestCredentialDetailsAuthResult,
          weak_ptr_factory_.GetWeakPtr(), ids, std::move(callback),
          web_contents->GetWeakPtr()));
}

void PasswordsPrivateDelegateImpl::OnFetchingFamilyMembersCompleted(
    FetchFamilyResultsCallback callback,
    std::vector<password_manager::RecipientInfo> family_members,
    FetchFamilyMembersRequestStatus request_status) {
  api::passwords_private::FamilyFetchResults results;
  switch (request_status) {
    case FetchFamilyMembersRequestStatus::kUnknown:
    case FetchFamilyMembersRequestStatus::kNetworkError:
    case FetchFamilyMembersRequestStatus::kPendingRequest:
      results.status = api::passwords_private::FamilyFetchStatus::kUnknownError;
      break;
    case FetchFamilyMembersRequestStatus::kSuccess:
    case FetchFamilyMembersRequestStatus::kNoOtherFamilyMembers:
      // TODO(crbug.com/40268194): Add new FamilyFetchStatus and its handling.
      results.status = api::passwords_private::FamilyFetchStatus::kSuccess;
      break;
    case FetchFamilyMembersRequestStatus::kNoFamily:
      results.status = api::passwords_private::FamilyFetchStatus::kNoMembers;
  }
  if (request_status == FetchFamilyMembersRequestStatus::kSuccess) {
    for (const password_manager::RecipientInfo& family_member :
         family_members) {
      api::passwords_private::RecipientInfo recipient_info;
      recipient_info.user_id = family_member.user_id;
      recipient_info.email = family_member.email;
      recipient_info.display_name = family_member.user_name;
      recipient_info.profile_image_url = family_member.profile_image_url;

      if (!family_member.public_key.key.empty()) {
        recipient_info.is_eligible = true;
        api::passwords_private::PublicKey public_key;
        public_key.value = family_member.public_key.key;
        public_key.version = family_member.public_key.key_version;
        recipient_info.public_key = std::move(public_key);
      }

      results.family_members.push_back(std::move(recipient_info));
    }
  }
  std::move(callback).Run(results);
}

void PasswordsPrivateDelegateImpl::OsReauthTimeoutCall() {
#if !BUILDFLAG(IS_LINUX)
  if (event_router_) {
    event_router_->OnPasswordManagerAuthTimeout();
  }
#endif
}

void PasswordsPrivateDelegateImpl::SetCredentials(
    std::vector<CredentialUIEntry> credentials) {
  // Create lists of PasswordUiEntry and ExceptionEntry objects to send to
  // observers.
  current_entries_.clear();
  current_exceptions_.clear();

  for (CredentialUIEntry& credential : credentials) {
    if (credential.blocked_by_user) {
      api::passwords_private::ExceptionEntry current_exception_entry;
      current_exception_entry.urls =
          CreateUrlCollectionFromCredential(credential);
      current_exception_entry.id =
          credential_id_generator_.GenerateId(std::move(credential));
      current_exceptions_.push_back(std::move(current_exception_entry));
    } else {
      current_entries_.push_back(
          CreatePasswordUiEntryFromCredentialUiEntry(std::move(credential)));
    }
  }
  for (CredentialUIEntry& credential :
       saved_passwords_presenter_.GetBlockedSites()) {
    api::passwords_private::ExceptionEntry current_exception_entry;
    current_exception_entry.urls =
        CreateUrlCollectionFromCredential(credential);
    current_exception_entry.id =
        credential_id_generator_.GenerateId(std::move(credential));
    current_exceptions_.push_back(std::move(current_exception_entry));
  }

  if (current_entries_initialized_) {
    CHECK(get_saved_passwords_list_callbacks_.empty());
    CHECK(get_password_exception_list_callbacks_.empty());
  }

  if (event_router_) {
    event_router_->OnSavedPasswordsListChanged(current_entries_);
    event_router_->OnPasswordExceptionsListChanged(current_exceptions_);
  }

  current_entries_initialized_ = true;

  for (auto& callback : get_saved_passwords_list_callbacks_) {
    std::move(callback).Run(current_entries_);
  }
  get_saved_passwords_list_callbacks_.clear();
  for (auto& callback : get_password_exception_list_callbacks_) {
    std::move(callback).Run(current_exceptions_);
  }
  get_password_exception_list_callbacks_.clear();
}

void PasswordsPrivateDelegateImpl::MovePasswordsToAccount(
    const std::vector<int>& ids) {
  if (!password_manager::features_util::IsAccountStorageActive(sync_service_)) {
    return;
  }

  std::vector<CredentialUIEntry> credentials_to_move;
  credentials_to_move.reserve(ids.size());
  for (int id : ids) {
    const CredentialUIEntry* entry = credential_id_generator_.TryGetKey(id);
    if (!entry) {
      continue;
    }
    credentials_to_move.push_back(*entry);
  }

  saved_passwords_presenter_.MoveCredentialsToAccount(credentials_to_move);
}

void PasswordsPrivateDelegateImpl::FetchFamilyMembers(
    FetchFamilyResultsCallback callback) {
  if (!sharing_password_recipients_fetcher_) {
    sharing_password_recipients_fetcher_ =
        std::make_unique<password_manager::RecipientsFetcherImpl>(
            chrome::GetChannel(), url_loader_factory_, identity_manager_);
  }
  sharing_password_recipients_fetcher_->FetchFamilyMembers(base::BindOnce(
      &PasswordsPrivateDelegateImpl::OnFetchingFamilyMembersCompleted,
      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}

void PasswordsPrivateDelegateImpl::SharePassword(
    int id,
    const ShareRecipients& recipients) {
  const CredentialUIEntry* entry = credential_id_generator_.TryGetKey(id);
  if (!entry) {
    return;
  }

  std::vector<password_manager::PasswordForm> corresponding_credentials =
      password_manager::ToPasswordForms(
          saved_passwords_presenter_.GetCorrespondingStoredCredentials(*entry));
  if (corresponding_credentials.empty()) {
    return;
  }

  password_manager::PasswordSenderService* password_sender_service =
      password_sender_service_;
  for (const api::passwords_private::RecipientInfo& recipient_info :
       recipients) {
    CHECK(recipient_info.public_key.has_value());
    password_manager::PublicKey public_key;
    public_key.key = recipient_info.public_key.value().value;
    public_key.key_version = recipient_info.public_key.value().version;
    password_sender_service->SendPasswords(
        corresponding_credentials, {.user_id = recipient_info.user_id,
                                    .public_key = std::move(public_key)});
  }
}

void PasswordsPrivateDelegateImpl::ImportPasswords(
    api::passwords_private::PasswordStoreSet to_store,
    ImportResultsCallback results_callback,
    content::WebContents* web_contents) {
  DCHECK_NE(api::passwords_private::PasswordStoreSet::kDeviceAndAccount,
            to_store);
  password_manager::PasswordForm::Store store_to_use =
      *ConvertToPasswordFormStores(to_store).begin();
  password_import_controller_->Import(
      web_contents, store_to_use,
      base::BindOnce(&ConvertImportResults).Then(std::move(results_callback)));
}

void PasswordsPrivateDelegateImpl::ContinueImport(
    const std::vector<int>& selected_ids,
    ImportResultsCallback results_callback) {
  if (selected_ids.empty()) {
    password_import_controller_->ContinueImport(
        selected_ids, base::BindOnce(&ConvertImportResults)
                          .Then(std::move(results_callback)));
    return;
  }

  std::u16string message;
#if BUILDFLAG(IS_MAC)
  message = l10n_util::GetStringUTF16(
      IDS_PASSWORDS_PAGE_IMPORT_AUTHENTICATION_PROMPT_BIOMETRIC_SUFFIX);
#elif BUILDFLAG(IS_WIN)
  message = l10n_util::GetStringUTF16(
      IDS_PASSWORDS_PAGE_IMPORT_AUTHENTICATION_PROMPT);
#endif

  AuthenticateUser(
      base::Seconds(0), message,
      base::BindOnce(&PasswordsPrivateDelegateImpl::OnImportPasswordsAuthResult,
                     weak_ptr_factory_.GetWeakPtr(),
                     std::move(results_callback), selected_ids));
}

void PasswordsPrivateDelegateImpl::ResetImporter(bool delete_file) {
  password_import_controller_->ResetImporter(delete_file);
}

void PasswordsPrivateDelegateImpl::ExportPasswords(
    base::OnceCallback<void(ExportPasswordsResult)> accepted_callback,
    content::WebContents* web_contents) {
  std::u16string message;
#if BUILDFLAG(IS_MAC)
  message = l10n_util::GetStringUTF16(
      IDS_PASSWORDS_PAGE_EXPORT_AUTHENTICATION_PROMPT_BIOMETRIC_SUFFIX);
#elif BUILDFLAG(IS_WIN)
  message = l10n_util::GetStringUTF16(
      IDS_PASSWORDS_PAGE_EXPORT_AUTHENTICATION_PROMPT);
#endif

  AuthenticateUser(
      base::Seconds(0), message,
      base::BindOnce(&PasswordsPrivateDelegateImpl::OnExportPasswordsAuthResult,
                     weak_ptr_factory_.GetWeakPtr(),
                     std::move(accepted_callback), web_contents->GetWeakPtr()));
}

api::passwords_private::ExportProgressStatus
PasswordsPrivateDelegateImpl::GetExportProgressStatus() {
  return ConvertStatus(password_export_controller_->GetExportProgressStatus());
}

bool PasswordsPrivateDelegateImpl::IsAccountStorageActive() {
  return password_manager::features_util::IsAccountStorageActive(sync_service_);
}

std::vector<api::passwords_private::PasswordUiEntry>
PasswordsPrivateDelegateImpl::GetInsecureCredentials() {
  return password_check_delegate_.GetInsecureCredentials();
}

std::vector<api::passwords_private::PasswordUiEntryList>
PasswordsPrivateDelegateImpl::GetCredentialsWithReusedPassword() {
  return password_check_delegate_.GetCredentialsWithReusedPassword();
}

bool PasswordsPrivateDelegateImpl::MuteInsecureCredential(
    const api::passwords_private::PasswordUiEntry& credential) {
  return password_check_delegate_.MuteInsecureCredential(credential);
}

bool PasswordsPrivateDelegateImpl::UnmuteInsecureCredential(
    const api::passwords_private::PasswordUiEntry& credential) {
  return password_check_delegate_.UnmuteInsecureCredential(credential);
}

void PasswordsPrivateDelegateImpl::StartPasswordCheck(
    StartPasswordCheckCallback callback) {
  password_check_delegate_.StartPasswordCheck(
      password_manager::LeakDetectionInitiator::kBulkSyncedPasswordsCheck,
      std::move(callback));
  if (!trust_safety_sentiment_service_) {
    return;
  }
  trust_safety_sentiment_service_->RanPasswordCheck();
}

std::optional<password_manager::CredentialUIEntry>
PasswordsPrivateDelegateImpl::GetCredentialFromId(int credential_id) {
  const password_manager::CredentialUIEntry* credential =
      credential_id_generator_.TryGetKey(credential_id);
  if (!credential) {
    return std::nullopt;
  }
  return *credential;
}

api::passwords_private::PasswordCheckStatus
PasswordsPrivateDelegateImpl::GetPasswordCheckStatus() {
  return password_check_delegate_.GetPasswordCheckStatus();
}

void PasswordsPrivateDelegateImpl::SwitchBiometricAuthBeforeFillingState(
    AuthenticationCallback authentication_callback) {
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
  AuthResultCallback callback =
      base::BindOnce(&ChangeBiometricAuthenticationBeforeFillingSetting, prefs_,
                     std::move(authentication_callback));

  AuthenticateUser(
      base::Seconds(0),
      GetMessageForBiometricAuthenticationBeforeFillingSetting(prefs_),
      std::move(callback));
#else
  NOTIMPLEMENTED();
#endif
}

void PasswordsPrivateDelegateImpl::ShowAddShortcutDialog(
    content::WebContents* web_contents) {
  BrowserWindowInterface* browser =
      GlobalBrowserCollection::GetInstance()->FindBrowserWithTab(web_contents);
  DCHECK(browser);
  web_app::CreateWebAppFromCurrentWebContents(
      browser, web_app::WebAppInstallFlow::kInstallSite);
  base::UmaHistogramEnumeration(
      "PasswordManager.ShortcutMetric",
      password_manager::metrics_util::PasswordManagerShortcutMetric::
          kAddShortcutClicked);
}

void PasswordsPrivateDelegateImpl::ShowLastExportedFileInShell(
    content::WebContents* web_contents) {
  password_export_controller_->ShowLastExportedFileInShell(web_contents);
}

void PasswordsPrivateDelegateImpl::ChangePasswordManagerPin(
    content::WebContents* web_contents,
    base::OnceCallback<void(bool)> success_callback) {
  ChangePinController* controller =
      ChangePinController::ForWebContents(web_contents);
  if (controller) {
    controller->StartChangePin(std::move(success_callback));
  }
}

void PasswordsPrivateDelegateImpl::IsPasswordManagerPinAvailable(
    content::WebContents* web_contents,
    base::OnceCallback<void(bool)> pin_available_callback) {
  ChangePinController* controller =
      ChangePinController::ForWebContents(web_contents);
  if (!controller) {
    std::move(pin_available_callback).Run(false);
    return;
  }
  controller->IsChangePinFlowAvailable(std::move(pin_available_callback));
}

void PasswordsPrivateDelegateImpl::DisconnectCloudAuthenticator(
    base::OnceCallback<void(bool)> success_callback) {
  if (enclave_manager_) {
    enclave_manager_->Unenroll(std::move(success_callback));
  }
}

bool PasswordsPrivateDelegateImpl::IsConnectedToCloudAuthenticator() {
  if (!enclave_manager_) {
    return false;
  }

  return enclave_manager_->IsRegistered();
}

password_manager::ActionableError
PasswordsPrivateDelegateImpl::GetActionableError() {
  return password_manager::GetActionableErrorFromPasswordStores(
      account_password_store_.get(), profile_password_store_.get());
}

void PasswordsPrivateDelegateImpl::DeleteAllPasswordManagerData(
    base::OnceCallback<void(bool)> success_callback) {
  std::u16string message;
#if BUILDFLAG(IS_MAC)
  message = l10n_util::GetStringFUTF16(
      IDS_PASSWORDS_PAGE_DELETE_ALL_DATA_AUTHENTICATION_PROMPT_BIOMETRIC_SUFFIX,
      l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_BRAND_NAME));
#elif BUILDFLAG(IS_WIN)
  message = l10n_util::GetStringUTF16(
      IDS_PASSWORDS_PAGE_DELETE_ALL_DATA_AUTHENTICATION_PROMPT);
#endif

  AuthenticateUser(
      base::Seconds(0), message,
      base::BindOnce(&PasswordsPrivateDelegateImpl::OnDeleteAllDataAuthResult,
                     weak_ptr_factory_.GetWeakPtr(),
                     std::move(success_callback)));
}

void PasswordsPrivateDelegateImpl::OnDeleteAllDataAuthResult(
    base::OnceCallback<void(bool)> success_callback,
    bool authenticated) {
  if (!authenticated) {
    std::move(success_callback).Run(false);
    return;
  }

  saved_passwords_presenter_.DeleteAllData(std::move(success_callback));

  // Record password removal from both stores. "Delete all" requires UI
  // confirmation and re-authentication, indicating strong user intent to
  // remove all password data.
  AddPasswordRemovalReason(
      prefs_, password_manager::IsAccountStore(true),
      password_manager::metrics_util::PasswordManagerCredentialRemovalReason::
          kDeleteAllPasswordManagerData);
  AddPasswordRemovalReason(
      prefs_, password_manager::IsAccountStore(false),
      password_manager::metrics_util::PasswordManagerCredentialRemovalReason::
          kDeleteAllPasswordManagerData);
}

base::WeakPtr<PasswordsPrivateDelegate>
PasswordsPrivateDelegateImpl::AsWeakPtr() {
  return weak_ptr_factory_.GetWeakPtr();
}

void PasswordsPrivateDelegateImpl::CopyPlaintextBackupPassword(
    int id,
    base::OnceCallback<void(bool)> callback) {
  AuthenticateUser(
      kPasswordManagerAuthValidity,
      GetReauthPurpose(api::passwords_private::PlaintextReason::kCopy),
      base::BindOnce(
          &PasswordsPrivateDelegateImpl::OnCopyBackupPasswordAuthResult,
          weak_ptr_factory_.GetWeakPtr(), id, std::move(callback)));
}

password_manager::InsecureCredentialsManager*
PasswordsPrivateDelegateImpl::GetInsecureCredentialsManager() {
  return password_check_delegate_.GetInsecureCredentialsManager();
}

void PasswordsPrivateDelegateImpl::RestartAuthTimer() {
  auth_timeout_handler_.RestartAuthTimer();
}

void PasswordsPrivateDelegateImpl::MaybeShowPasswordShareButtonIPH(
    base::WeakPtr<content::WebContents> web_contents) {
  if (!web_contents) {
    return;
  }
  BrowserWindowInterface* browser =
      GlobalBrowserCollection::GetInstance()->FindBrowserWithTab(
          web_contents.get());
  if (!browser || !browser->GetWindow()) {
    return;
  }
  BrowserUserEducationInterface::From(browser)->MaybeShowFeaturePromo(
      feature_engagement::kIPHPasswordSharingFeature);
}

void PasswordsPrivateDelegateImpl::OnPasswordsExportProgress(
    const password_manager::PasswordExportInfo& progress) {
  if (!base::FeatureList::IsEnabled(
          password_manager::features::kEnablePasswordManagerMojoApi)) {
    if (event_router_) {
      event_router_->OnPasswordsExportProgress(ConvertStatus(progress.status),
                                               progress.file_path,
                                               progress.folder_name);
    }
    return;
  }

  observers_.Notify(
      &PasswordsPrivateDelegate::Observer::OnPasswordsExportProgress,
      progress.status, progress.folder_name);
}

void PasswordsPrivateDelegateImpl::OnRequestPlaintextPasswordAuthResult(
    int id,
    api::passwords_private::PlaintextReason reason,
    PlaintextPasswordCallback callback,
    bool authenticated) {
  if (!authenticated) {
    std::move(callback).Run(std::nullopt);
    return;
  }

  const CredentialUIEntry* entry = credential_id_generator_.TryGetKey(id);
  if (!entry) {
    std::move(callback).Run(std::nullopt);
    return;
  }

  if (reason == api::passwords_private::PlaintextReason::kCopy) {
    WriteToClipboardAndScheduleClear(entry->password);

    // In case of copy we don't need to give password back to UI. callback
    // will receive either empty string in case of success or null otherwise.
    // Copying occurs here so javascript doesn't need plaintext password.
    std::move(callback).Run(std::u16string());
  } else {
    std::move(callback).Run(entry->password);
  }
  EmitHistogramsForCredentialAccess(*entry, reason);
}

void PasswordsPrivateDelegateImpl::ClearClipboard(
    ui::ClipboardSequenceNumberToken sequence_number) {
  if (ui::Clipboard::GetForCurrentThread()->GetSequenceNumber(
          ui::ClipboardBuffer::kCopyPaste) == sequence_number) {
    ui::Clipboard::GetForCurrentThread()->Clear(
        ui::ClipboardBuffer::kCopyPaste);
  }
}

void PasswordsPrivateDelegateImpl::WriteToClipboardAndScheduleClear(
    const std::u16string& password) {
  {
    // ScopedClipboardWriter commits to the clipboard on destruction.
    // This block ensures the clipboard sequence number is updated before we
    // capture it.
    ui::ScopedClipboardWriter clipboard_writer(ui::ClipboardBuffer::kCopyPaste);
    clipboard_writer.WriteText(password);
    clipboard_writer.MarkAsConfidential();
  }

  // Start timer to clear clipboard.
  clipboard_clear_timer_.Start(
      FROM_HERE, kClipboardClearDelay,
      base::BindOnce(&PasswordsPrivateDelegateImpl::ClearClipboard,
                     weak_ptr_factory_.GetWeakPtr(),
                     ui::Clipboard::GetForCurrentThread()->GetSequenceNumber(
                         ui::ClipboardBuffer::kCopyPaste)));
}

void PasswordsPrivateDelegateImpl::OnCopyBackupPasswordAuthResult(
    int id,
    base::OnceCallback<void(bool)> callback,
    bool authenticated) {
  if (!authenticated) {
    std::move(callback).Run(false);
    return;
  }

  const CredentialUIEntry* entry = credential_id_generator_.TryGetKey(id);
  if (!entry || !entry->backup_password) {
    std::move(callback).Run(false);
    return;
  }

  WriteToClipboardAndScheduleClear(entry->backup_password->value);

  std::move(callback).Run(true);
}

void PasswordsPrivateDelegateImpl::OnRequestCredentialDetailsAuthResult(
    const std::vector<int>& ids,
    UiEntriesCallback callback,
    base::WeakPtr<content::WebContents> web_contents,
    bool authenticated) {
  if (!authenticated || !web_contents) {
    std::move(callback).Run({});
    return;
  }

  CredentialUIEntry last_entry;
  std::vector<api::passwords_private::PasswordUiEntry> passwords;
  for (int id : ids) {
    const CredentialUIEntry* credential =
        credential_id_generator_.TryGetKey(id);
    if (!credential) {
      continue;
    }

    api::passwords_private::PasswordUiEntry password_ui_entry =
        CreatePasswordUiEntryFromCredentialUiEntry(*credential);
    password_ui_entry.password = base::UTF16ToUTF8(credential->password);
    password_ui_entry.note = base::UTF16ToUTF8(credential->note);
    // password_manager::MovePasswordsToAccountStore() takes care of moving the
    // entire equivalence class, so passing the first element is fine.
    passwords.push_back(std::move(password_ui_entry));

    last_entry = *credential;
  }

  if (!passwords.empty()) {
    EmitHistogramsForCredentialAccess(
        last_entry, api::passwords_private::PlaintextReason::kView);
  }
  std::move(callback).Run(std::move(passwords));

  // Attempt to show "Password Share Button" help-bubble when the user opens
  // PasswordsDetailsSection. The task is posted with a delay because WebUI is
  // rendered asynchronously and help-bubble anchor might be registered with
  // some delay.
  base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
      FROM_HERE,
      base::BindOnce(
          &PasswordsPrivateDelegateImpl::MaybeShowPasswordShareButtonIPH,
          weak_ptr_factory_.GetWeakPtr(), web_contents),
      base::Milliseconds(300));
}

void PasswordsPrivateDelegateImpl::OnExportPasswordsAuthResult(
    base::OnceCallback<void(ExportPasswordsResult)> accepted_callback,
    base::WeakPtr<content::WebContents> web_contents,
    bool authenticated) {
  if (!authenticated || !web_contents) {
    std::move(accepted_callback).Run(ExportPasswordsResult::kReauthFailed);
    return;
  }

  bool accepted = password_export_controller_->Export(web_contents.get());
  std::move(accepted_callback)
      .Run(accepted ? ExportPasswordsResult::kSuccess
                    : ExportPasswordsResult::kInProgress);
}

void PasswordsPrivateDelegateImpl::OnImportPasswordsAuthResult(
    ImportResultsCallback results_callback,
    const std::vector<int>& selected_ids,
    bool authenticated) {
  if (!authenticated) {
    password_manager::ImportResults result;
    result.status = password_manager::ImportResults::DISMISSED;
    std::move(results_callback).Run(ConvertImportResults(result));
    return;
  }

  CHECK(password_import_controller_);
  password_import_controller_->ContinueImport(
      selected_ids,
      base::BindOnce(&ConvertImportResults).Then(std::move(results_callback)));
}

void PasswordsPrivateDelegateImpl::OnStateChanged(
    syncer::SyncService* sync_service) {
  if (event_router_) {
    event_router_->OnAccountStorageActiveStateChanged(IsAccountStorageActive());
  }
}

void PasswordsPrivateDelegateImpl::OnSyncShutdown(syncer::SyncService* sync) {
  sync_service_observation_.Reset();
}

void PasswordsPrivateDelegateImpl::OnReauthCompleted(bool authenticated) {
  device_authenticator_.reset();

  auth_timeout_handler_.OnUserReauthenticationResult(authenticated);
}

void PasswordsPrivateDelegateImpl::OnSavedPasswordsChanged(
    const password_manager::PasswordStoreChangeList& changes) {
  SetCredentials(saved_passwords_presenter_.GetSavedCredentials());
}

void PasswordsPrivateDelegateImpl::OnLoginsChanged(
    password_manager::PasswordStoreInterface*,
    const password_manager::PasswordStoreChangeList&) {}

void PasswordsPrivateDelegateImpl::OnLoginsRetained(
    password_manager::PasswordStoreInterface*,
    const std::vector<password_manager::StoredCredential>&) {}

void PasswordsPrivateDelegateImpl::OnErrorStateChanged(
    password_manager::PasswordStoreInterface* store,
    password_manager::ActionableError error) {
  if (event_router_) {
    event_router_->OnPasswordManagerActionableErrorChanged(
        ToActionableApiError(GetActionableError()));
  }
}

void PasswordsPrivateDelegateImpl::OnWebAppInstalledWithOsHooks(
    const webapps::AppId& app_id) {
  if (app_id != ash::kPasswordManagerAppId) {
    return;
  }
  // Post task with delay because new browser window for an app isn't created
  // yet.
  base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
      FROM_HERE, maybe_show_profile_switch_iph_cb_, base::Seconds(1));
  base::UmaHistogramEnumeration(
      "PasswordManager.ShortcutMetric",
      password_manager::metrics_util::PasswordManagerShortcutMetric::
          kShortcutInstalled);
}

void PasswordsPrivateDelegateImpl::OnWebAppInstallManagerDestroyed() {
  install_manager_observation_.Reset();
}

void PasswordsPrivateDelegateImpl::EmitHistogramsForCredentialAccess(
    const CredentialUIEntry& entry,
    api::passwords_private::PlaintextReason reason) {
  UMA_HISTOGRAM_ENUMERATION(
      "PasswordManager.AccessPasswordInSettings",
      ConvertPlaintextReason(reason),
      password_manager::metrics_util::ACCESS_PASSWORD_COUNT);
}

void PasswordsPrivateDelegateImpl::AuthenticateUser(
    base::TimeDelta auth_validity_period,
    const std::u16string& message,
    AuthResultCallback auth_callback) {
  auto callback = password_manager::metrics_util::TimeCallbackMediumTimes(
      std::move(auth_callback), "PasswordManager.Settings.AuthenticationTime2");

#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_CHROMEOS)
  std::move(callback).Run(true);
#else

  // Authentication on Windows cannot be canceled.
  // TODO(crbug.com/40241199): Remove Cancel and instead simply destroy
  // |device_authenticator_|.
  if (device_authenticator_) {
#if BUILDFLAG(IS_WIN)
    // `device_authenticator_` lives as long as the authentication is in
    // progress. Since there is currently no way of canceling authentication
    // if the new one wants to start, new authentications will be resolved as if
    // they failed until the pending authentication gets resolved by the user.
    std::move(callback).Run(false);
    return;
#else
    device_authenticator_->Cancel();
#endif
  }
  device_authenticator_ = GetDeviceAuthenticator(auth_validity_period);

  AuthResultCallback on_reauth_completed =
      base::BindOnce(&PasswordsPrivateDelegateImpl::OnReauthCompleted,
                     weak_ptr_factory_.GetWeakPtr());

  auto pass_through = base::BindOnce(
      [](AuthResultCallback callback, bool auth_result) {
        std::move(callback).Run(auth_result);
        return auth_result;
      },
      std::move(callback));

  device_authenticator_->AuthenticateWithMessage(
      message, std::move(pass_through).Then(std::move(on_reauth_completed)));
#endif
}

api::passwords_private::PasswordUiEntry
PasswordsPrivateDelegateImpl::CreatePasswordUiEntryFromCredentialUiEntry(
    CredentialUIEntry credential) {
  api::passwords_private::PasswordUiEntry entry;
  std::ranges::transform(credential.GetAffiliatedDomains(),
                         std::back_inserter(entry.affiliated_domains),
                         [](const CredentialUIEntry::DomainInfo& domain) {
                           api::passwords_private::DomainInfo domain_info;
                           // `domain.name` is used to redirect to the Password
                           // Manager page for the password represented by the
                           // current `CredentialUIEntry`.
                           // LINT.IfChange
                           domain_info.name = domain.name;
                           // LINT.ThenChange(//chrome/browser/ui/passwords/bubble_controllers/manage_passwords_bubble_controller.cc)
                           domain_info.url = domain.url.spec();
                           domain_info.signon_realm = domain.signon_realm;
                           return domain_info;
                         });
  entry.is_passkey = !credential.passkey_credential_id.empty();
  entry.username = base::UTF16ToUTF8(credential.username);
  if (entry.is_passkey) {
    entry.display_name = base::UTF16ToUTF8(credential.user_display_name);
  }
  if (credential.creation_time.has_value()) {
    entry.creation_time =
        credential.creation_time->InMillisecondsSinceUnixEpoch();
  }
  entry.stored_in = extensions::StoreSetFromCredential(credential);
  if (credential.federation_origin.IsValid()) {
    std::u16string formatted_origin =
        url_formatter::FormatUrlForSecurityDisplay(
            credential.federation_origin.GetURL(),
            url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);

    entry.federation_text = base::UTF16ToUTF8(formatted_origin);
  }
  std::optional<GURL> change_password_url = credential.GetChangePasswordURL();
  if (change_password_url.has_value()) {
    entry.change_password_url = change_password_url->spec();
  }

  if (credential.backup_password.has_value()) {
    api::passwords_private::BackupPasswordInfo backup_password_info;
    backup_password_info.value =
        base::UTF16ToUTF8(credential.backup_password->value);
    backup_password_info.creation_date = base::UTF16ToUTF8(
        base::i18n::IcuBridge::GetInstance().date_time_formatter().Format(
            credential.backup_password->creation_timestamp,
            base::i18n::datetime_options::MD::Medium()));
    entry.backup_password = std::move(backup_password_info);
  }
  entry.hidden = credential.hidden;
  if (base::FeatureList::IsEnabled(
          password_manager::features::
              kPasswordCompromiseWarningInDetailsCard) &&
      !credential.password_issues.empty()) {
    entry.compromised_info = CreateCompromiseInfo(credential);
  }
  entry.id = credential_id_generator_.GenerateId(std::move(credential));
  return entry;
}

}  // namespace extensions
