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

#import "ios/chrome/browser/autofill/ui_bundled/autofill_app_interface.h"

#import "base/apple/foundation_util.h"
#import "base/check.h"
#import "base/functional/callback_helpers.h"
#import "base/memory/singleton.h"
#import "base/strings/sys_string_conversions.h"
#import "base/strings/utf_string_conversions.h"
#import "base/test/ios/wait_util.h"
#import "base/uuid.h"
#import "components/application_locale_storage/application_locale_storage.h"
#import "components/autofill/core/browser/data_manager/addresses/address_data_manager.h"
#import "components/autofill/core/browser/data_manager/autofill_ai/entity_data_manager.h"
#import "components/autofill/core/browser/data_manager/payments/payments_data_manager.h"
#import "components/autofill/core/browser/data_manager/personal_data_manager.h"
#import "components/autofill/core/browser/data_model/addresses/autofill_profile_test_api.h"
#import "components/autofill/core/browser/data_model/addresses/autofill_structured_address_component.h"
#import "components/autofill/core/browser/data_model/autofill_ai/entity_instance.h"
#import "components/autofill/core/browser/field_types.h"
#import "components/autofill/core/browser/form_import/form_data_importer.h"
#import "components/autofill/core/browser/form_import/payments/payments_form_data_importer.h"
#import "components/autofill/core/browser/foundations/autofill_client.h"
#import "components/autofill/core/browser/foundations/browser_autofill_manager_test_api.h"
#import "components/autofill/core/browser/payments/credit_card_save_manager.h"
#import "components/autofill/core/browser/payments/payments_autofill_client.h"
#import "components/autofill/core/browser/payments/payments_network_interface.h"
#import "components/autofill/core/browser/payments/virtual_card_enrollment_manager.h"
#import "components/autofill/core/browser/test_utils/autofill_test_utils.h"
#import "components/autofill/core/browser/test_utils/entity_data_test_utils.h"
#import "components/autofill/core/common/autofill_prefs.h"
#import "components/autofill/ios/browser/autofill_driver_ios.h"
#import "components/autofill/ios/browser/autofill_java_script_feature.h"
#import "components/autofill/ios/browser/credit_card_save_manager_test_observer_bridge.h"
#import "components/autofill/ios/browser/credit_card_util.h"
#import "components/autofill/ios/browser/ios_test_event_waiter.h"
#import "components/keyed_service/core/service_access_type.h"
#import "components/password_manager/core/browser/password_manager_util.h"
#import "components/password_manager/core/browser/password_store/password_form_converters.h"
#import "components/password_manager/core/browser/password_store/password_store_consumer.h"
#import "components/password_manager/core/browser/password_store/password_store_interface.h"
#import "ios/chrome/browser/autofill/atmemory/public/at_memory_commands.h"
#import "ios/chrome/browser/autofill/model/personal_data_manager_factory.h"
#import "ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.h"
#import "ios/chrome/browser/passwords/model/ios_chrome_profile_password_store_factory.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
#import "ios/chrome/common/ui/reauthentication/mock_reauthentication_module.h"
#import "ios/chrome/test/app/chrome_test_util.h"
#import "ios/chrome/test/app/tab_test_util.h"
#import "ios/public/provider/chrome/browser/risk_data/risk_data_api.h"
#import "ios/web/public/js_messaging/web_frames_manager.h"
#import "ios/web/public/web_state.h"
#import "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#import "services/network/test/test_url_loader_factory.h"

namespace {

const char16_t kExampleUsername[] = u"concrete username";
const char16_t kExamplePassword[] = u"concrete password";
const char16_t kExampleBackupPassword[] = u"backup password";

// Gets the current profile password store.
scoped_refptr<password_manager::PasswordStoreInterface>
GetPasswordProfileStore() {
  // ServiceAccessType governs behaviour in Incognito: only modifications with
  // EXPLICIT_ACCESS, which correspond to user's explicit gesture, succeed.
  // This test does not deal with Incognito, and should not run in Incognito
  // context. Therefore IMPLICIT_ACCESS is used to let the test fail if in
  // Incognito context.
  return IOSChromeProfilePasswordStoreFactory::GetForProfile(
      chrome_test_util::GetOriginalProfile(),
      ServiceAccessType::IMPLICIT_ACCESS);
}

// This class is used to obtain results from the PasswordStore and hence both
// check the success of store updates and ensure that store has finished
// processing.
class TestStoreConsumer : public password_manager::PasswordStoreConsumer {
 public:
  void OnGetPasswordStoreResultsOrErrorFrom(
      password_manager::PasswordStoreInterface* store,
      password_manager::LoginsResultOrError results_or_error) override {
    if (std::holds_alternative<password_manager::PasswordStoreBackendError>(
            results_or_error)) {
      obtained_ = std::vector<password_manager::PasswordForm>();
    } else {
      obtained_ = password_manager::ToPasswordForms(
          std::get<password_manager::LoginsResult>(
              std::move(results_or_error)));
    }
  }

  const std::vector<password_manager::PasswordForm>& GetStoreResults() {
    results_.clear();
    obtained_.reset();
    GetPasswordProfileStore()->GetAllLogins(weak_ptr_factory_.GetWeakPtr());
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-result"
    base::test::ios::WaitUntilConditionOrTimeout(
        base::test::ios::kWaitForFileOperationTimeout, ^bool {
          return obtained_.has_value();
        });
#pragma clang diagnostic pop
    if (obtained_.has_value()) {
      results_ = std::move(obtained_.value());
      obtained_.reset();
    }
    return results_;
  }

 private:
  // Temporary cache of obtained store results.
  std::optional<std::vector<password_manager::PasswordForm>> obtained_;

  // Combination of fillable and blocked credentials from the store.
  std::vector<password_manager::PasswordForm> results_;

  base::WeakPtrFactory<TestStoreConsumer> weak_ptr_factory_{this};
};

// Saves `form` to the profile password store and waits until the async
// processing is done.
void SaveToPasswordProfileStore(const password_manager::PasswordForm& form) {
  GetPasswordProfileStore()->AddLogin(password_manager::FromPasswordForm(form));
  // When we retrieve the form from the store, `in_store` should be set.
  password_manager::PasswordForm expected_form = form;
  expected_form.in_store = password_manager::PasswordForm::Store::kProfileStore;
  // Check the result and ensure PasswordStore processed this.
  TestStoreConsumer consumer;
  for (const auto& result : consumer.GetStoreResults()) {
    if (result == expected_form) {
      return;
    }
  }
}

// Creates an example form for the passed URL.
password_manager::PasswordForm CreateExamplePasswordForm(
    const GURL& url = GURL("https://example.com/")) {
  password_manager::PasswordForm password_form;
  password_form.username_value = kExampleUsername;
  password_form.password_value = kExamplePassword;
  password_form.url = url;
  password_form.signon_realm =
      password_manager_util::GetSignonRealm(password_form.url);
  return password_form;
}

// Saves an example form in the profile store.
void SaveExamplePasswordFormInProfileStore() {
  password_manager::PasswordForm example = CreateExamplePasswordForm();
  SaveToPasswordProfileStore(std::move(example));
}

// Saves an example form in the profile store for the passed URL.
void SaveLocalPasswordForm(const GURL& url) {
  password_manager::PasswordForm local_form = CreateExamplePasswordForm(url);
  SaveToPasswordProfileStore(std::move(local_form));
}

// Saves an example form with a backup password in the profile store for the
// passed URL.
void SavePasswordFormWithBackup(const GURL& url) {
  password_manager::PasswordForm password_form = CreateExamplePasswordForm(url);
  password_form.SetPasswordBackupNote(kExampleBackupPassword);
  SaveToPasswordProfileStore(std::move(password_form));
}

// Removes all credentials from the profile store.
void ClearProfilePasswordStore() {
  GetPasswordProfileStore()->RemoveLoginsCreatedBetween(
      FROM_HERE, base::Time(), base::Time(), base::DoNothing());
  TestStoreConsumer consumer;
}

// Saves an example profile in the store.
void AddAutofillProfile(autofill::PersonalDataManager* personalDataManager,
                        std::optional<autofill::AutofillProfile::RecordType>
                            recordType = std::nullopt) {
  autofill::AutofillProfile profile = autofill::test::GetFullProfile();
  // If the test profile is already in the store, adding it will be a no-op.
  // In that case, early return.
  for (const autofill::AutofillProfile* p :
       personalDataManager->address_data_manager().GetProfiles()) {
    if (p->Compare(profile) == 0) {
      return;
    }
  }
  size_t profileCount =
      personalDataManager->address_data_manager().GetProfiles().size();

  if (recordType.has_value()) {
    test_api(profile).set_record_type(recordType.value());
  }
  personalDataManager->address_data_manager().AddProfile(profile);

  ConditionBlock conditionBlock = ^bool {
    return profileCount <
           personalDataManager->address_data_manager().GetProfiles().size();
  };
  CHECK(base::test::ios::WaitUntilConditionOrTimeout(
      base::test::ios::kWaitForActionTimeout, conditionBlock));
}

}  // namespace

namespace autofill {

class VirtualCardEnrollmentManager;

// Helper class that provides access to private members of
// BrowserAutofillManager, FormDataImporter and CreditCardSaveManager. This
// class is friend with some autofill internal classes to access private fields.
class FakeCreditCardServer : public CreditCardSaveManager::ObserverForTest {
 public:
  static FakeCreditCardServer* SharedInstance() {
    return base::Singleton<FakeCreditCardServer>::get();
  }

  FakeCreditCardServer() {}
  ~FakeCreditCardServer() override {}
  FakeCreditCardServer(const FakeCreditCardServer&) = delete;
  FakeCreditCardServer& operator=(const FakeCreditCardServer&) = delete;

  // Access the CreditCardSaveManager.
  static CreditCardSaveManager* GetCreditCardSaveManager() {
    return GetAutofillClient()
        .GetFormDataImporter()
        ->GetPaymentsFormDataImporter()
        .GetCreditCardSaveManager();
  }

  // Access the VirtualCardEnrollmentManager.
  static VirtualCardEnrollmentManager* GetVirtualCardEnrollmentManager() {
    return GetAutofillClient()
        .GetPaymentsAutofillClient()
        ->GetVirtualCardEnrollmentManager();
  }

  // Access the PaymentsNetworkInterface.
  static payments::PaymentsNetworkInterface* GetPaymentsNetworkInterface() {
    return GetAutofillClient()
        .GetPaymentsAutofillClient()
        ->GetPaymentsNetworkInterface();
  }

  static AutofillClient& GetAutofillClient() {
    web::WebState* web_state = chrome_test_util::GetCurrentWebState();
    web::WebFramesManager* frames_manager =
        autofill::AutofillJavaScriptFeature::GetInstance()->GetWebFramesManager(
            web_state);
    web::WebFrame* main_frame = frames_manager->GetMainWebFrame();
    DCHECK(web_state);
    return AutofillDriverIOS::FromWebStateAndWebFrame(web_state, main_frame)
        ->GetAutofillManager()
        .client();
  }

  // Delete all failed attempds registered on every cards.
  static void ClearCreditCardSaveStrikes() {
    GetCreditCardSaveManager()
        ->GetCreditCardSaveStrikeDatabase()
        ->ClearAllStrikes();
  }

  static void ClearVirtualCardEnrollmentStrikes() {
    GetVirtualCardEnrollmentManager()->ClearAllStrikesForTesting();
  }

  // Set the number of failed attempds registered on a card.
  static void SetFormFillMaxStrikes(NSString* card_key, int max) {
    // The strike key is made of CreditCardSaveStrikeDatabase's project prefix
    // and the last 4 digits of the card used in fillAndSubmitForm(), which can
    // be found in credit_card_upload_form_address_and_cc.html.
    GetCreditCardSaveManager()
        ->GetCreditCardSaveStrikeDatabase()
        ->strike_database_->SetStrikeData(base::SysNSStringToUTF8(card_key),
                                          max);
  }

  // Reset the IOSTestEventWaiter and make it watch `events`.
  void ResetEventWaiterForEvents(NSArray* events, base::TimeDelta timeout) {
    std::list<CreditCardSaveManagerObserverEvent> events_list;
    for (NSNumber* e : events) {
      events_list.push_back(
          static_cast<CreditCardSaveManagerObserverEvent>([e intValue]));
    }
    event_waiter_ = std::make_unique<
        IOSTestEventWaiter<CreditCardSaveManagerObserverEvent>>(
        std::move(events_list), timeout);
  }

  void OnOfferLocalSave() override {
    OnEvent(CreditCardSaveManagerObserverEvent::kOnOfferLocalSaveCalled);
  }

  void OnDecideToRequestUploadSave() override {
    OnEvent(
        CreditCardSaveManagerObserverEvent::kOnDecideToRequestUploadSaveCalled);
  }

  void OnReceivedGetUploadDetailsResponse() override {
    OnEvent(CreditCardSaveManagerObserverEvent::
                kOnReceivedGetUploadDetailsResponseCalled);
  }

  void OnSentUploadCardRequest() override {
    OnEvent(CreditCardSaveManagerObserverEvent::kOnSentUploadCardRequestCalled);
  }

  void OnReceivedUploadCardResponse() override {
    OnEvent(CreditCardSaveManagerObserverEvent::
                kOnReceivedUploadCardResponseCalled);
  }

  void OnShowCardSavedFeedback() override {
    OnEvent(CreditCardSaveManagerObserverEvent::kOnShowCardSavedFeedbackCalled);
  }

  void OnStrikeChangeComplete() override {
    OnEvent(CreditCardSaveManagerObserverEvent::kOnStrikeChangeCompleteCalled);
  }

  // Triggers `event` on the IOSTestEventWaiter.
  bool OnEvent(CreditCardSaveManagerObserverEvent event) {
    return event_waiter_->OnEvent(event);
  }

  // Waits until the expected events are triggered.
  bool WaitForEvents() { return event_waiter_->Wait(); }

  // Sets the response to payment server requests.
  void SetPaymentsResponse(NSString* request, NSString* response, int error) {
    test_url_loader_factory_->AddResponse(
        base::SysNSStringToUTF8(request), base::SysNSStringToUTF8(response),
        static_cast<net::HttpStatusCode>(error));
  }

  void ClearPaymentsResponses() { test_url_loader_factory_->ClearResponses(); }

  void SetPaymentsRiskData(const std::string& risk_data) {
    GetCreditCardSaveManager()->OnDidGetUploadRiskData(risk_data);
  }

  void SetUp() {
    test_url_loader_factory_ =
        std::make_unique<network::TestURLLoaderFactory>();
    // Set up the URL loader factory for the PaymentsNetworkInterface so we can
    // intercept those network requests.
    shared_url_loader_factory_ =
        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
            test_url_loader_factory_.get());

    payments::PaymentsNetworkInterface* payments_network_interface =
        FakeCreditCardServer::GetPaymentsNetworkInterface();
    payments_network_interface->set_url_loader_factory_for_testing(
        shared_url_loader_factory_);

    // Set a fake access token to avoid fetch requests.
    payments_network_interface->set_access_token_for_testing(
        "fake_access_token");

    // Observe actions in CreditCardSaveManager.
    CreditCardSaveManager* credit_card_save_manager =
        FakeCreditCardServer::GetCreditCardSaveManager();
    credit_card_save_manager->SetEventObserverForTesting(this);
  }

  void TearDown() {
    ClearCreditCardSaveStrikes();
    CreditCardSaveManager* credit_card_save_manager =
        FakeCreditCardServer::GetCreditCardSaveManager();
    credit_card_save_manager->SetEventObserverForTesting(nullptr);
    event_waiter_.reset();
    shared_url_loader_factory_.reset();
    test_url_loader_factory_.reset();
  }

 private:
  std::unique_ptr<network::TestURLLoaderFactory> test_url_loader_factory_;
  scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
  std::unique_ptr<IOSTestEventWaiter<CreditCardSaveManagerObserverEvent>>
      event_waiter_;
};
}  // namespace autofill

@implementation AutofillAppInterface

+ (void)clearProfilePasswordStore {
  ClearProfilePasswordStore();
}

+ (void)saveExamplePasswordFormToProfileStore {
  SaveExamplePasswordFormInProfileStore();
}

+ (void)savePasswordFormForURLSpec:(NSString*)URLSpec {
  SaveLocalPasswordForm(GURL(base::SysNSStringToUTF8(URLSpec)));
}

+ (void)savePasswordFormWithBackupForURLSpec:(NSString*)URLSpec {
  SavePasswordFormWithBackup(GURL(base::SysNSStringToUTF8(URLSpec)));
}

+ (NSInteger)profilesCount {
  autofill::PersonalDataManager* personalDataManager =
      [self personalDataManager];
  return personalDataManager->address_data_manager().GetProfiles().size();
}

+ (BOOL)isAccountProfileAtIndex:(NSInteger)index {
  CHECK_LT(index, self.profilesCount);

  autofill::PersonalDataManager* personalDataManager =
      [self personalDataManager];
  return personalDataManager->address_data_manager()
      .GetProfiles()[index]
      ->IsAccountProfile();
}

+ (void)clearProfilesStore {
  ProfileIOS* profileIOS = chrome_test_util::GetOriginalProfile();
  autofill::AddressDataManager& addressDataManager =
      autofill::PersonalDataManagerFactory::GetForProfile(profileIOS)
          ->address_data_manager();
  for (const autofill::AutofillProfile* profile :
       addressDataManager.GetProfiles()) {
    addressDataManager.RemoveProfile(profile->guid());
  }

  ConditionBlock conditionBlock = ^bool {
    return 0 == addressDataManager.GetProfiles().size();
  };
  CHECK(base::test::ios::WaitUntilConditionOrTimeout(
      base::test::ios::kWaitForActionTimeout, conditionBlock));

  autofill::prefs::SetAutofillProfileEnabled(profileIOS->GetPrefs(), YES);
}

+ (void)saveExampleProfile {
  AddAutofillProfile([self personalDataManager]);
}

+ (void)saveExampleAccountProfile {
  AddAutofillProfile([self personalDataManager],
                     autofill::AutofillProfile::RecordType::kAccount);
}

+ (void)saveExampleHomeAndWorkAccountProfile {
  AddAutofillProfile([self personalDataManager],
                     autofill::AutofillProfile::RecordType::kAccountHome);
}

+ (void)saveExampleAccountNameEmailProfile {
  AddAutofillProfile([self personalDataManager],
                     autofill::AutofillProfile::RecordType::kAccountNameEmail);
}

+ (NSString*)exampleProfileName {
  autofill::AutofillProfile profile = autofill::test::GetFullProfile();
  std::u16string name = profile.GetInfo(
      autofill::AutofillType(autofill::NAME_FULL),
      GetApplicationContext()->GetApplicationLocaleStorage()->Get());
  return base::SysUTF16ToNSString(name);
}

+ (NSString*)exampleProfileAddress {
  autofill::AutofillProfile profile = autofill::test::GetFullProfile();
  std::u16string address = profile.GetInfo(
      autofill::AutofillType(autofill::ADDRESS_HOME_LINE1),
      GetApplicationContext()->GetApplicationLocaleStorage()->Get());
  return base::SysUTF16ToNSString(address);
}

+ (NSString*)exampleProfileCity {
  autofill::AutofillProfile profile = autofill::test::GetFullProfile();
  std::u16string city = profile.GetInfo(
      autofill::AutofillType(autofill::ADDRESS_HOME_CITY),
      GetApplicationContext()->GetApplicationLocaleStorage()->Get());
  return base::SysUTF16ToNSString(city);
}

+ (NSString*)exampleProfileState {
  autofill::AutofillProfile profile = autofill::test::GetFullProfile();
  std::u16string state = profile.GetInfo(
      autofill::AutofillType(autofill::ADDRESS_HOME_STATE),
      GetApplicationContext()->GetApplicationLocaleStorage()->Get());
  return base::SysUTF16ToNSString(state);
}

+ (NSString*)exampleProfileZip {
  autofill::AutofillProfile profile = autofill::test::GetFullProfile();
  std::u16string zip = profile.GetInfo(
      autofill::AutofillType(autofill::ADDRESS_HOME_ZIP),
      GetApplicationContext()->GetApplicationLocaleStorage()->Get());
  return base::SysUTF16ToNSString(zip);
}

+ (void)clearCreditCardStore {
  autofill::PaymentsDataManager& paymentsDataManager =
      [self personalDataManager]->payments_data_manager();
  for (const autofill::CreditCard* creditCard :
       paymentsDataManager.GetCreditCards()) {
    // This will not remove server cards, as they have no guid.
    paymentsDataManager.RemoveByGUID(creditCard->guid());
  }

  ProfileIOS* profile = chrome_test_util::GetOriginalProfile();
  autofill::prefs::SetAutofillPaymentMethodsEnabled(profile->GetPrefs(), YES);
}

// Clears all server data including server cards.
+ (void)clearAllServerDataForTesting {
  [self personalDataManager]
      ->payments_data_manager()
      .ClearAllServerDataForTesting();
}

+ (NSString*)saveLocalCreditCard {
  autofill::PersonalDataManager* personalDataManager =
      [self personalDataManager];
  autofill::CreditCard card = autofill::test::GetCreditCard();
  size_t card_count =
      personalDataManager->payments_data_manager().GetCreditCards().size();
  personalDataManager->payments_data_manager().AddCreditCard(card);
  ConditionBlock conditionBlock = ^bool {
    return card_count <
           personalDataManager->payments_data_manager().GetCreditCards().size();
  };
  CHECK(base::test::ios::WaitUntilConditionOrTimeout(
      base::test::ios::kWaitForFileOperationTimeout, conditionBlock));
  personalDataManager->NotifyPersonalDataObserver();
  return base::SysUTF16ToNSString(card.NetworkAndLastFourDigits());
}

+ (NSString*)saveLocalCreditCardWithCvc {
  autofill::PersonalDataManager* personalDataManager =
      [self personalDataManager];
  autofill::CreditCard card =
      autofill::test::WithCvc(autofill::test::GetCreditCard());
  size_t card_count =
      personalDataManager->payments_data_manager().GetCreditCards().size();
  personalDataManager->payments_data_manager().AddCreditCard(card);
  ConditionBlock conditionBlock = ^bool {
    return card_count <
           personalDataManager->payments_data_manager().GetCreditCards().size();
  };
  CHECK(base::test::ios::WaitUntilConditionOrTimeout(
      base::test::ios::kWaitForFileOperationTimeout, conditionBlock));
  personalDataManager->NotifyPersonalDataObserver();
  return base::SysUTF16ToNSString(card.NetworkAndLastFourDigits());
}

+ (NSInteger)localCreditCount {
  return [self personalDataManager]
      ->payments_data_manager()
      .GetCreditCards()
      .size();
}

+ (NSString*)firstLocalCreditCardCvc {
  autofill::PaymentsDataManager& paymentsDataManager =
      [self personalDataManager]->payments_data_manager();
  const std::vector<const autofill::CreditCard*>& cards =
      paymentsDataManager.GetLocalCreditCards();
  if (cards.empty()) {
    return nil;
  }
  return autofill::GetCreditCardCvcString(*cards[0]);
}

+ (NSString*)exampleCreditCardName {
  autofill::CreditCard card = autofill::test::GetCreditCard();
  return base::SysUTF16ToNSString(
      card.GetRawInfo(autofill::CREDIT_CARD_NAME_FULL));
}

+ (NSString*)exampleCreditCardNumber {
  autofill::CreditCard card = autofill::test::GetCreditCard();
  return base::SysUTF16ToNSString(
      card.GetRawInfo(autofill::CREDIT_CARD_NUMBER));
}

+ (NSString*)saveMaskedCreditCard {
  autofill::PersonalDataManager* personalDataManager =
      [self personalDataManager];
  autofill::CreditCard card =
      autofill::test::WithCvc(autofill::test::GetMaskedServerCard());
  DCHECK(card.record_type() != autofill::CreditCard::RecordType::kLocalCard);
  personalDataManager->payments_data_manager().AddServerCreditCardForTest(
      std::make_unique<autofill::CreditCard>(card));
  personalDataManager->NotifyPersonalDataObserver();
  return base::SysUTF16ToNSString(card.NetworkAndLastFourDigits());
}

+ (NSString*)saveMaskedCreditCardEnrolledInVirtualCard {
  autofill::PersonalDataManager* personalDataManager =
      [self personalDataManager];
  size_t card_count =
      personalDataManager->payments_data_manager().GetCreditCards().size();
  autofill::CreditCard card =
      autofill::test::GetMaskedServerCardEnrolledIntoVirtualCardNumber();
  CHECK_NE(card.record_type(), autofill::CreditCard::RecordType::kLocalCard);

  personalDataManager->payments_data_manager().AddServerCreditCardForTest(
      std::make_unique<autofill::CreditCard>(card));

  // Confirm card is present in personalDataManager
  CHECK(base::test::ios::WaitUntilConditionOrTimeout(
      base::test::ios::kWaitForFileOperationTimeout, ^bool {
        return (personalDataManager->payments_data_manager()
                    .GetCreditCards()
                    .size() == card_count + 1);
      }));

  personalDataManager->NotifyPersonalDataObserver();
  return base::SysUTF16ToNSString(card.NetworkAndLastFourDigits());
}

+ (NSString*)saveMaskedCreditCardEnrolledInCardInfoRetrieval {
  autofill::PersonalDataManager* personalDataManager =
      [self personalDataManager];
  autofill::CreditCard card =
      autofill::test::GetMaskedServerCardEnrolledIntoRuntimeRetrieval();
  CHECK_NE(card.record_type(), autofill::CreditCard::RecordType::kLocalCard);

  personalDataManager->payments_data_manager().AddServerCreditCardForTest(
      std::make_unique<autofill::CreditCard>(card));

  personalDataManager->NotifyPersonalDataObserver();
  return base::SysUTF16ToNSString(card.NetworkAndLastFourDigits());
}

+ (void)setUpFakeCreditCardServer {
  autofill::FakeCreditCardServer::SharedInstance()->SetUp();
}

+ (void)tearDownFakeCreditCardServer {
  autofill::FakeCreditCardServer::SharedInstance()->TearDown();
}

// Clears the virtual card enrollment strike data.
+ (void)clearVirtualCardEnrollmentStrikes {
  autofill::FakeCreditCardServer::SharedInstance()
      ->ClearVirtualCardEnrollmentStrikes();
}

+ (void)resetEventWaiterForEvents:(NSArray*)events
                          timeout:(base::TimeDelta)timeout {
  autofill::FakeCreditCardServer::SharedInstance()->ResetEventWaiterForEvents(
      events, timeout);
}

+ (BOOL)waitForEvents {
  return autofill::FakeCreditCardServer::SharedInstance()->WaitForEvents();
}

+ (void)setPaymentsResponse:(NSString*)response
                 forRequest:(NSString*)request
              withErrorCode:(int)error {
  return autofill::FakeCreditCardServer::SharedInstance()->SetPaymentsResponse(
      request, response, error);
}

+ (void)clearPaymentsResponses {
  return autofill::FakeCreditCardServer::SharedInstance()
      ->ClearPaymentsResponses();
}

+ (void)setAccessToken {
  if (autofill::payments::PaymentsNetworkInterface* payments_network_interface =
          autofill::FakeCreditCardServer::GetPaymentsNetworkInterface()) {
    // Set a fake access token to avoid fetch requests.
    payments_network_interface->set_access_token_for_testing(
        "fake_access_token");
  }
}

+ (void)setFormFillMaxStrikes:(int)max forCard:(NSString*)card {
  return autofill::FakeCreditCardServer::SharedInstance()
      ->SetFormFillMaxStrikes(card, max);
}

+ (void)setPaymentsRiskData:(NSString*)riskData {
  return autofill::FakeCreditCardServer::SharedInstance()->SetPaymentsRiskData(
      base::SysNSStringToUTF8(riskData));
}

+ (void)considerCreditCardFormSecureForTesting {
  static_cast<autofill::ChromeAutofillClientIOS&>(
      *autofill::AutofillClientIOS::FromWebState(
          chrome_test_util::GetCurrentWebState()))
      .ConsiderAsSecureForTesting();
}

+ (NSString*)paymentsRiskData {
  return ios::provider::GetRiskData();
}

+ (void)setMandatoryReauthEnabled:(BOOL)enabled {
  autofill::PersonalDataManager* personalDataManager =
      [self personalDataManager];
  personalDataManager->payments_data_manager()
      .SetPaymentMethodsMandatoryReauthEnabled(enabled);
}

+ (void)setPaymentCvcStorageEnabled:(BOOL)enabled {
  autofill::PersonalDataManager* personalDataManager =
      [self personalDataManager];
  personalDataManager->payments_data_manager().SetPaymentsCvcStorageEnabled(
      enabled);
}

+ (NSString*)saveRedressNumberEntityWithName:(NSString*)name
                                      number:(NSString*)number {
  if (!name || !number) {
    return nil;
  }

  autofill::EntityDataManager* entityDataManager = [self entityDataManager];
  if (!entityDataManager) {
    return nil;
  }

  autofill::test::RedressNumberOptions options = {};
  std::u16string name_u16;
  std::u16string number_u16;
  base::Uuid uuid = base::Uuid::GenerateRandomV4();
  name_u16 = base::SysNSStringToUTF16(name);
  options.name = name_u16.c_str();
  number_u16 = base::SysNSStringToUTF16(number);
  options.number = number_u16.c_str();
  std::string guid_str = uuid.AsLowercaseString();
  options.guid = guid_str;
  autofill::EntityInstance entity =
      autofill::test::GetRedressNumberEntityInstance(options);
  entityDataManager->AddOrUpdateEntityInstance(entity);
  return base::SysUTF8ToNSString(guid_str);
}

+ (void)removeEntityWithUUID:(NSString*)uuid {
  autofill::EntityDataManager* entityDataManager = [self entityDataManager];
  if (!entityDataManager) {
    return;
  }
  entityDataManager->RemoveEntityInstance(
      autofill::EntityInstance::EntityId(base::SysNSStringToUTF8(uuid)));
}

+ (void)removeEntityModifiedSince:(NSDate*)time {
  autofill::EntityDataManager* entityDataManager = [self entityDataManager];
  if (!entityDataManager) {
    return;
  }
  entityDataManager->RemoveEntityInstancesModifiedBetween(
      base::Time::FromNSDate(time), base::Time::Now());
}

+ (BOOL)savePassportEntity {
  autofill::EntityDataManager* entityDataManager = [self entityDataManager];
  if (!entityDataManager) {
    return NO;
  }

  autofill::EntityInstance entity = autofill::test::GetPassportEntityInstance();
  entityDataManager->AddOrUpdateEntityInstance(entity);
  return YES;
}

+ (NSString*)saveServerWalletPassportEntity {
  autofill::EntityDataManager* entityDataManager = [self entityDataManager];
  if (!entityDataManager) {
    return nil;
  }

  autofill::test::PassportEntityOptions options = {};
  base::Uuid uuid = base::Uuid::GenerateRandomV4();
  std::string guid_str = uuid.AsLowercaseString();
  options.guid = guid_str;
  options.record_type = autofill::EntityInstance::RecordType::kServerWallet;

  autofill::EntityInstance entity =
      autofill::test::GetPassportEntityInstance(options);
  autofill::EntityInstance masked_entity =
      autofill::test::MaskEntityInstance(entity);
  entityDataManager->AddOrUpdateEntityInstance(masked_entity);
  return base::SysUTF8ToNSString(guid_str);
}

+ (BOOL)saveVehicleEntity {
  autofill::EntityDataManager* entityDataManager = [self entityDataManager];
  if (!entityDataManager) {
    return NO;
  }

  autofill::EntityInstance entity = autofill::test::GetVehicleEntityInstance();
  size_t entity_count = entityDataManager->GetEntityInstances().size();
  entityDataManager->AddOrUpdateEntityInstance(entity);

  ConditionBlock conditionBlock = ^bool {
    return entity_count < entityDataManager->GetEntityInstances().size();
  };

  CHECK(base::test::ios::WaitUntilConditionOrTimeout(
      base::test::ios::kWaitForFileOperationTimeout, conditionBlock));
  return YES;
}

#pragma mark - Private

// The PersonalDataManager instance for the current profile.
+ (autofill::PersonalDataManager*)personalDataManager {
  ProfileIOS* profile = chrome_test_util::GetOriginalProfile();
  autofill::PersonalDataManager* personalDataManager =
      autofill::PersonalDataManagerFactory::GetForProfile(profile);
  personalDataManager->payments_data_manager().SetSyncingForTest(true);
  return personalDataManager;
}

+ (void)showAutofillAiSaveEntityBubble {
  autofill::EntityInstance entity = autofill::test::GetVehicleEntityInstance();
  autofill::ChromeAutofillClientIOS* client =
      static_cast<autofill::ChromeAutofillClientIOS*>(
          autofill::AutofillClientIOS::FromWebState(
              chrome_test_util::GetCurrentWebState()));
  if (client) {
    client->ShowEntityImportBubble(std::move(entity), std::nullopt,
                                   /*save_is_synchronous=*/true,
                                   base::DoNothing());
  }
}

+ (void)showAtMemoryUI {
  id<AtMemoryCommands> atMemoryHandler = HandlerForProtocol(
      chrome_test_util::GetMainBrowser()->GetCommandDispatcher(),
      AtMemoryCommands);
  [atMemoryHandler showAtMemory];
}

+ (autofill::EntityDataManager*)entityDataManager {
  return autofill::FakeCreditCardServer::GetAutofillClient()
      .GetEntityDataManager();
}

namespace {

autofill::AutofillDriverIOS* GetMainFrameAutofillDriver() {
  web::WebState* web_state = chrome_test_util::GetCurrentWebState();
  if (!web_state) {
    return nullptr;
  }
  web::WebFramesManager* frames_manager =
      autofill::AutofillJavaScriptFeature::GetInstance()->GetWebFramesManager(
          web_state);
  if (!frames_manager) {
    return nullptr;
  }
  web::WebFrame* main_frame = frames_manager->GetMainWebFrame();
  if (!main_frame) {
    return nullptr;
  }
  return autofill::AutofillDriverIOS::FromWebStateAndWebFrame(web_state,
                                                              main_frame);
}

}  // namespace

+ (BOOL)isFormCachedInMainFrame {
  autofill::AutofillDriverIOS* driver = GetMainFrameAutofillDriver();
  if (!driver) {
    return NO;
  }
  return !autofill::test_api(driver->GetAutofillManager())
              .form_structures()
              .empty();
}

+ (BOOL)waitForFormToBeCachedInMainFrame {
  if (![self isFormCachedInMainFrame]) {
    if (autofill::AutofillDriverIOS* driver = GetMainFrameAutofillDriver()) {
      driver->ScanForms(/*immediately=*/true);
    }
  }

  ConditionBlock condition = ^BOOL {
    return [self isFormCachedInMainFrame];
  };
  return base::test::ios::WaitUntilConditionOrTimeout(
      base::test::ios::kWaitForPageLoadTimeout, condition);
}

@end
