// Copyright 2024 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/ios_chrome_payments_autofill_client.h"

#import "base/functional/callback_helpers.h"
#import "base/test/metrics/histogram_tester.h"
#import "base/test/mock_callback.h"
#import "base/test/scoped_feature_list.h"
#import "base/uuid.h"
#import "components/autofill/core/browser/data_model/payments/credit_card.h"
#import "components/autofill/core/browser/metrics/payments/credit_card_save_metrics.h"
#import "components/autofill/core/browser/payments/autofill_error_dialog_context.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/ui/payments/bubble_show_options.h"
#import "components/autofill/core/browser/ui/payments/virtual_card_enroll_ui_model.h"
#import "components/autofill/core/common/autofill_payments_features.h"
#import "components/autofill/core/common/autofill_prefs.h"
#import "components/autofill/ios/browser/autofill_agent.h"
#import "components/autofill/ios/browser/test_autofill_client_ios.h"
#import "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/autofill/autofill_ai/error_dialog/model/autofill_ai_error_dialog_context.h"
#import "ios/chrome/browser/autofill/autofill_ai/public/save_entity_params.h"
#import "ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.h"
#import "ios/chrome/browser/autofill/model/manual_fill_virtual_card_cache.h"
#import "ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.h"
#import "ios/chrome/browser/infobars/model/infobar_ios.h"
#import "ios/chrome/browser/infobars/model/infobar_manager_impl.h"
#import "ios/chrome/browser/infobars/model/infobar_type.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/chrome/browser/shared/public/commands/autofill_commands.h"
#import "ios/chrome/test/ios_chrome_scoped_testing_local_state.h"
#import "ios/web/public/test/web_state_test_util.h"
#import "ios/web/public/test/web_task_environment.h"
#import "ios/web/public/web_state.h"
#import "testing/gmock/include/gmock/gmock.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#import "third_party/ocmock/gtest_support.h"
#import "ui/base/l10n/l10n_util.h"

@interface FakeAutofillCommands : NSObject <AutofillCommands>

// Returns the model provided to showVirtualCardEnrollmentBottomSheet
- (std::unique_ptr<autofill::VirtualCardEnrollUiModel>)
    getVirtualCardEnrollUiModel;

// Returns the error context provided to showAutofillErrorDialog.
- (const std::optional<autofill::AutofillErrorDialogContext>&)
    autofillErrorDialogContext;

// Returns whether showSaveCardBottomSheet was called.
- (BOOL)showSaveCardBottomSheetCalled;
@end

@implementation FakeAutofillCommands {
  std::unique_ptr<autofill::VirtualCardEnrollUiModel> _virtualCardEnrollUiModel;
  std::optional<autofill::AutofillErrorDialogContext> _errorContext;
  BOOL _showSaveCardBottomSheet;
}

- (std::unique_ptr<autofill::VirtualCardEnrollUiModel>)
    getVirtualCardEnrollUiModel {
  return std::move(_virtualCardEnrollUiModel);
}

- (const std::optional<autofill::AutofillErrorDialogContext>&)
    autofillErrorDialogContext {
  return _errorContext;
}

- (BOOL)showSaveCardBottomSheetCalled {
  return _showSaveCardBottomSheet;
}

#pragma mark - AutofillCommands

- (void)showCardUnmaskAuthentication {
}
- (void)dismissCardUnmaskAuthentication {
}
- (void)continueCardUnmaskWithOtpAuth {
}
- (void)continueCardUnmaskWithCvcAuth {
}
- (void)showCredentialBottomSheet:(const autofill::FormActivityParams&)params {
}
- (void)showPaymentsBottomSheet:(const autofill::FormActivityParams&)params {
}
- (void)showScanCardSaveAndFillBottomSheet:
    (const autofill::FormActivityParams&)params {
}
- (void)dismissPaymentAndScanCardSheets {
}
- (void)showSaveCardBottomSheetOnOriginWebState:(web::WebState*)originWebState {
  _showSaveCardBottomSheet = YES;
}

- (void)dismissSaveCardBottomSheet {
}

- (void)showWalletReminderNoticeOnOriginWebState:(web::WebState*)originWebState
                               legalMessageLines:(autofill::LegalMessageLines)
                                                     legalMessageLines {
}

- (void)showVirtualCardEnrollmentBottomSheet:
            (std::unique_ptr<autofill::VirtualCardEnrollUiModel>)model
                              originWebState:(web::WebState*)originWebState {
  _virtualCardEnrollUiModel = std::move(model);
}
- (void)dismissVirtualCardEnrollmentBottomSheet {
}

- (void)showEditAddressBottomSheet {
}

- (void)dismissEditAddressBottomSheet {
}

- (void)legacyResetAutofillSuggestionsLoadingStates {
}

- (void)showAutofillErrorDialog:
    (autofill::AutofillErrorDialogContext)errorContext {
  _errorContext = std::move(errorContext);
}

- (void)dismissAutofillErrorDialog {
}

- (void)showAutofillAiErrorDialog:
    (autofill::AutofillAiErrorDialogContext)errorContext {
}

- (void)dismissAutofillAiErrorDialog {
}

- (void)showAutofillProgressDialog {
}
- (void)dismissAutofillProgressDialog {
}

- (void)showSaveEntityDialog:(autofill::SaveEntityParams)params {
  std::move(params.callback)
      .Run(autofill::AutofillClient::AutofillAiBubbleResult::kUnknown,
           std::nullopt, {});
}

- (void)dismissSaveEntityDialog {
}

- (void)showAmbientAutofillNotice:(const autofill::FormActivityParams&)params {
  // TODO(crbug.com/533502803): Implement displaying of the ambient notice
  // sheet.
}

- (void)dismissAmbientAutofillNotice {
  // TODO(crbug.com/533502803): Implement dismissal of the ambient notice sheet.
}

- (void)showAutofillAIPrivateInferenceNotice {
  // TODO(crbug.com/552031299): Implement displaying of the private inference
  // notice bottom sheet.
}

- (void)dismissAutofillAIPrivateInferenceNotice {
  // TODO(crbug.com/552031299): Implement dismissal of the private inference
  // notice bottom sheet.
}

@end

namespace autofill {
namespace {

using ::testing::_;

class TestChromeAutofillClient
    : public WithFakedFromWebState<ChromeAutofillClientIOS> {
 public:
  explicit TestChromeAutofillClient(ProfileIOS* profile,
                                    web::WebState* web_state,
                                    infobars::InfoBarManager* infobar_manager,
                                    AutofillAgent* autofill_agent)
      : WithFakedFromWebState<ChromeAutofillClientIOS>(profile,
                                                       web_state,
                                                       infobar_manager,
                                                       autofill_agent) {
  }

  void RemoveAutofillSaveCardInfoBar() override {
    removed_save_card_infobar_ = true;
  }

  bool DidRemoveSaveCardInfobar() { return removed_save_card_infobar_; }

  void set_last_committed_primary_main_frame_url(const GURL& url) {
    last_committed_primary_main_frame_url_ = url;
  }

  const GURL& GetLastCommittedPrimaryMainFrameURL() const override {
    if (!last_committed_primary_main_frame_url_.is_empty()) {
      return last_committed_primary_main_frame_url_;
    }
    return ChromeAutofillClientIOS::GetLastCommittedPrimaryMainFrameURL();
  }

 private:
  bool removed_save_card_infobar_ = false;
  GURL last_committed_primary_main_frame_url_;
};

class IOSChromePaymentsAutofillClientTest : public PlatformTest {
 private:
  web::WebTaskEnvironment task_environment_;
  IOSChromeScopedTestingLocalState scoped_testing_local_state_;

 public:
  void SetUp() override {
    PlatformTest::SetUp();
    profile_ = TestProfileIOS::Builder().Build();
    web::WebState::CreateParams params(profile_.get());
    web_state_ = web::WebState::Create(params);
    web_state_->GetView();
    web_state_->SetKeepRenderProcessAlive(true);
    InfoBarManagerImpl::CreateForWebState(web_state_.get());
    infobars::InfoBarManager* infobar_manager =
        InfoBarManagerImpl::FromWebState(web_state_.get());
    autofill_agent_ =
        [[AutofillAgent alloc] initWithPrefService:profile_->GetPrefs()
                                          webState:web_state_.get()];
    autofill_client_ = std::make_unique<TestChromeAutofillClient>(
        profile_.get(), web_state_.get(), infobar_manager, autofill_agent_);

    // Inject the autofill commands fake into the AutofillTabHelper and
    // ChromeAutofillClient.
    autofill_commands_ = [[FakeAutofillCommands alloc] init];
    AutofillBottomSheetTabHelper::CreateForWebState(web_state_.get());
    bottomsheet_tab_helper_ =
        AutofillBottomSheetTabHelper::FromWebState(web_state_.get());
    bottomsheet_tab_helper_->SetAutofillBottomSheetHandler(autofill_commands_);

    autofill_client_->set_commands_handler(autofill_commands_);
  }

  void TearDown() override {
    bottomsheet_tab_helper_ = nullptr;
    autofill_commands_ = nil;
    autofill_agent_ = nil;
    autofill_client_.reset();
    web_state_.reset();
    profile_.reset();
    PlatformTest::TearDown();
  }

  TestChromeAutofillClient* client() { return autofill_client_.get(); }

  FakeAutofillCommands* autofill_commands() { return autofill_commands_; }

  payments::IOSChromePaymentsAutofillClient* payments_client() {
    return client()->GetPaymentsAutofillClient();
  }

  std::unique_ptr<VirtualCardEnrollUiModel> ShowVirtualCardEnrollDialog() {
    payments_client()->ShowVirtualCardEnrollDialog(
        VirtualCardEnrollmentFields(),
        /*accept_virtual_card_callback=*/base::DoNothing(),
        /*decline_virtual_card_callback=*/base::DoNothing());
    std::unique_ptr<VirtualCardEnrollUiModel> ui_model =
        [autofill_commands_ getVirtualCardEnrollUiModel];
    return ui_model;
  }

 protected:
  FakeAutofillCommands* autofill_commands_;
  std::unique_ptr<TestProfileIOS> profile_;

 private:
  AutofillAgent* autofill_agent_;
  std::unique_ptr<TestChromeAutofillClient> autofill_client_;

 protected:
  std::unique_ptr<web::WebState> web_state_;
  raw_ptr<AutofillBottomSheetTabHelper> bottomsheet_tab_helper_;
};

// Test that on credit card upload completed successfully with infobar showing,
// `AutofillSaveCardInfoBarDelegateIOS.credit_card_upload_completed_` is set and
// runs
// `AutofillSaveCardInfoBarDelegateIOS.credit_card_upload_completion_callback_`
// with card saved.
TEST_F(IOSChromePaymentsAutofillClientTest,
       CreditCardUploadCompleted_CardSaved_WithInfobar) {
  // Shows card upload in an infobar for a card with 1 strike.
  payments_client()->ShowSaveCreditCardToCloud(
      test::GetCreditCard(), LegalMessageLines(),
      payments::PaymentsAutofillClient::SaveCreditCardOptions()
          .with_num_strikes(1)
          .with_show_prompt(true),
      base::DoNothing());

  // Sets credit card upload completion callback that gets executed with the
  // save card result as saved.
  base::MockCallback<base::OnceCallback<void(bool card_saved)>>
      mock_credit_card_upload_completion_callback;
  client()
      ->GetAutofillSaveCardInfoBarDelegateIOS()
      ->SetCreditCardUploadCompletionCallback(
          mock_credit_card_upload_completion_callback.Get());

  EXPECT_CALL(mock_credit_card_upload_completion_callback,
              Run(/*card_saved=*/true));
  payments_client()->CreditCardUploadCompleted(
      /*result=*/payments::PaymentsAutofillClient::PaymentsRpcResult::kSuccess,
      /*on_confirmation_closed_callback=*/std::nullopt);
  EXPECT_TRUE(client()
                  ->GetAutofillSaveCardInfoBarDelegateIOS()
                  ->IsCreditCardUploadComplete());
  EXPECT_FALSE(client()->DidRemoveSaveCardInfobar());
}

// Test that on credit card upload completed unsuccessfully with infobar
// showing, `AutofillSaveCardInfoBarDelegateIOS.credit_card_upload_completed_`
// is set and runs
// `AutofillSaveCardInfoBarDelegateIOS.credit_card_upload_completion_callback_`
// with card not saved and error context is set.
TEST_F(IOSChromePaymentsAutofillClientTest,
       CreditCardUploadCompleted_CardNotSaved_WithInfobar) {
  // Shows card upload in an infobar for a card with 1 strike.
  payments_client()->ShowSaveCreditCardToCloud(
      test::GetCreditCard(), LegalMessageLines(),
      payments::PaymentsAutofillClient::SaveCreditCardOptions()
          .with_num_strikes(1)
          .with_show_prompt(true),
      base::DoNothing());

  // Sets credit card upload completion callback that gets executed with the
  // save card result as not saved.
  base::MockCallback<base::OnceCallback<void(bool card_saved)>>
      mock_credit_card_upload_completion_callback;
  client()
      ->GetAutofillSaveCardInfoBarDelegateIOS()
      ->SetCreditCardUploadCompletionCallback(
          mock_credit_card_upload_completion_callback.Get());

  EXPECT_CALL(mock_credit_card_upload_completion_callback,
              Run(/*card_saved=*/false));
  payments_client()->CreditCardUploadCompleted(
      /*result=*/payments::PaymentsAutofillClient::PaymentsRpcResult::
          kPermanentFailure,
      /*on_confirmation_closed_callback=*/std::nullopt);
  EXPECT_TRUE(client()
                  ->GetAutofillSaveCardInfoBarDelegateIOS()
                  ->IsCreditCardUploadComplete());
  EXPECT_TRUE(client()->DidRemoveSaveCardInfobar());
  const std::optional<AutofillErrorDialogContext>& error_context =
      [autofill_commands() autofillErrorDialogContext];
  EXPECT_TRUE(error_context.has_value());
  EXPECT_EQ(error_context.value().type,
            AutofillErrorDialogType::kCreditCardUploadError);
}

// Test that on credit card upload's client-side timeout with infobar showing,
// `AutofillSaveCardInfoBarDelegateIOS.credit_card_upload_completed_` is set and
// runs
// `AutofillSaveCardInfoBarDelegateIOS.credit_card_upload_completion_callback_`
// with save card result as not saved and error context is not set.
TEST_F(
    IOSChromePaymentsAutofillClientTest,
    CreditCardUploadCompleted_ClientSideTimeout_WithInfobar_NoErrorConfirmation) {
  // Shows card upload in an infobar for a card with 1 strike.
  payments_client()->ShowSaveCreditCardToCloud(
      test::GetCreditCard(), LegalMessageLines(),
      payments::PaymentsAutofillClient::SaveCreditCardOptions()
          .with_num_strikes(1)
          .with_show_prompt(true),
      base::DoNothing());

  // Sets credit card upload completion callback that gets executed with the
  // save card result as not saved.
  base::MockCallback<base::OnceCallback<void(bool card_saved)>>
      mock_credit_card_upload_completion_callback;
  client()
      ->GetAutofillSaveCardInfoBarDelegateIOS()
      ->SetCreditCardUploadCompletionCallback(
          mock_credit_card_upload_completion_callback.Get());

  EXPECT_CALL(mock_credit_card_upload_completion_callback,
              Run(/*card_saved=*/false));
  payments_client()->CreditCardUploadCompleted(
      /*result=*/payments::PaymentsAutofillClient::PaymentsRpcResult::
          kClientSideTimeout,
      /*on_confirmation_closed_callback=*/std::nullopt);
  EXPECT_TRUE(client()
                  ->GetAutofillSaveCardInfoBarDelegateIOS()
                  ->IsCreditCardUploadComplete());
  EXPECT_TRUE(client()->DidRemoveSaveCardInfobar());
  const std::optional<AutofillErrorDialogContext>& error_context =
      [autofill_commands() autofillErrorDialogContext];
  EXPECT_FALSE(error_context.has_value());
}

// Test that a save CVC InfobarType is used when saving only a CVC for upload
// save.
TEST_F(IOSChromePaymentsAutofillClientTest,
       UsesSaveCvcInfobarTypeForUploadSaveCvc) {
  // Set up the save options for a CVC-only save.
  payments::PaymentsAutofillClient::SaveCreditCardOptions options;
  options.card_save_type =
      payments::PaymentsAutofillClient::CardSaveType::kCvcSaveOnly;
  options.show_prompt = true;

  payments_client()->ShowSaveCreditCardToCloud(
      test::GetCreditCard(), LegalMessageLines(), options, base::DoNothing());

  InfoBarManagerImpl* infobar_manager =
      InfoBarManagerImpl::FromWebState(web_state_.get());

  // Verify that a save CVC InfobarType was created.
  ASSERT_EQ(1u, infobar_manager->infobars().size());
  InfoBarIOS* infobar =
      static_cast<InfoBarIOS*>(infobar_manager->infobars()[0]);
  EXPECT_EQ(InfobarType::kInfobarTypeSaveCvc, infobar->infobar_type());
}

// Test that a save CVC InfobarType is used when saving only a CVC for local
// save.
TEST_F(IOSChromePaymentsAutofillClientTest,
       UsesSaveCvcInfobarTypeForLocalSaveCvc) {
  // Set up the save options for a CVC-only save.
  payments::PaymentsAutofillClient::SaveCreditCardOptions options;
  options.card_save_type =
      payments::PaymentsAutofillClient::CardSaveType::kCvcSaveOnly;
  options.show_prompt = true;

  payments_client()->ShowSaveCreditCardLocally(test::GetCreditCard(), options,
                                               base::DoNothing());

  InfoBarManagerImpl* infobar_manager =
      InfoBarManagerImpl::FromWebState(web_state_.get());

  // Verify that a save CVC InfobarType was created.
  ASSERT_EQ(1u, infobar_manager->infobars().size());
  InfoBarIOS* infobar =
      static_cast<InfoBarIOS*>(infobar_manager->infobars()[0]);
  EXPECT_EQ(InfobarType::kInfobarTypeSaveCvc, infobar->infobar_type());
}

TEST_F(IOSChromePaymentsAutofillClientTest,
       VirtualCardEnrollCompletedWithSucess) {
  std::unique_ptr<VirtualCardEnrollUiModel> ui_model =
      ShowVirtualCardEnrollDialog();

  payments_client()->VirtualCardEnrollCompleted(
      payments::PaymentsAutofillClient::PaymentsRpcResult::kSuccess);

  EXPECT_EQ(ui_model->enrollment_progress(),
            VirtualCardEnrollUiModel::EnrollmentProgress::kEnrolled);
}

TEST_F(IOSChromePaymentsAutofillClientTest,
       VirtualCardEnrollCompletedWithFailureSetsEnrollmentProgress) {
  std::unique_ptr<VirtualCardEnrollUiModel> ui_model =
      ShowVirtualCardEnrollDialog();

  payments_client()->VirtualCardEnrollCompleted(
      payments::PaymentsAutofillClient::PaymentsRpcResult::kPermanentFailure);

  EXPECT_EQ(ui_model->enrollment_progress(),
            VirtualCardEnrollUiModel::EnrollmentProgress::kFailed);
}

// Tests metrics for save card confirmation view shown for card not uploaded.
TEST_F(IOSChromePaymentsAutofillClientTest,
       ConfirmationViewShownForCardNotUploaded_Metrics) {
  base::HistogramTester histogram_tester;

  payments_client()->CreditCardUploadCompleted(
      /*result=*/payments::PaymentsAutofillClient::PaymentsRpcResult::
          kPermanentFailure,
      /*on_confirmation_closed_callback=*/std::nullopt);

  histogram_tester.ExpectUniqueSample(
      "Autofill.CreditCardUpload.ConfirmationShown.CardNotUploaded",
      /*is_shown=*/true, 1);
}

TEST_F(IOSChromePaymentsAutofillClientTest,
       VirtualCardEnrollCompletedWithFailureShowsErrorDialog) {
  ShowVirtualCardEnrollDialog();

  payments_client()->VirtualCardEnrollCompleted(
      payments::PaymentsAutofillClient::PaymentsRpcResult::kPermanentFailure);

  AutofillErrorDialogContext expected_context;
  expected_context.type =
      AutofillErrorDialogType::kVirtualCardEnrollmentTemporaryError;
  EXPECT_EQ([autofill_commands_ autofillErrorDialogContext],
            std::make_optional(expected_context));
}

TEST_F(IOSChromePaymentsAutofillClientTest,
       VirtualCardEnrollCompletedWithSuccessDoesNotShowAlert) {
  ShowVirtualCardEnrollDialog();

  payments_client()->VirtualCardEnrollCompleted(
      payments::PaymentsAutofillClient::PaymentsRpcResult::kSuccess);

  // Expect showAutofillErrorDialog has not been called.
  EXPECT_EQ([autofill_commands_ autofillErrorDialogContext], std::nullopt);
}

TEST_F(IOSChromePaymentsAutofillClientTest,
       VirtualCardEnrollCompletedWithClientSideTimeoutDoesNotShowAlert) {
  ShowVirtualCardEnrollDialog();

  payments_client()->VirtualCardEnrollCompleted(
      payments::PaymentsAutofillClient::PaymentsRpcResult::kClientSideTimeout);

  // Expect showAutofillErrorDialog has not been called.
  EXPECT_EQ([autofill_commands_ autofillErrorDialogContext], std::nullopt);
}

TEST_F(IOSChromePaymentsAutofillClientTest, ShowSaveCardBottomSheet) {
  payments_client()->ShowSaveCreditCardToCloud(
      test::GetCreditCard(), LegalMessageLines(),
      payments::PaymentsAutofillClient::SaveCreditCardOptions()
          .with_num_strikes(0)
          .with_show_prompt(true),
      base::DoNothing());
  EXPECT_TRUE([autofill_commands() showSaveCardBottomSheetCalled]);
}

TEST_F(IOSChromePaymentsAutofillClientTest,
       DoNoShowSaveCardBottomSheet_CardWithMoreThan0Strike) {
  base::HistogramTester histogram_tester;

  payments_client()->ShowSaveCreditCardToCloud(
      test::GetCreditCard(), LegalMessageLines(),
      payments::PaymentsAutofillClient::SaveCreditCardOptions()
          .with_num_strikes(1)
          .with_show_prompt(true),
      base::DoNothing());
  EXPECT_FALSE([autofill_commands() showSaveCardBottomSheetCalled]);

  histogram_tester.ExpectUniqueSample(
      "Autofill.SaveCreditCardPromptResult.IOS.Server.BottomSheet.NumStrikes.1."
      "NoFixFlow.SavingWithoutCvc",
      autofill_metrics::SaveCreditCardPromptResultIOS::kNotShown,
      /*expected_count=*/1);
}

TEST_F(IOSChromePaymentsAutofillClientTest,
       DoNoShowSaveCardBottomSheet_ForRequestingCardHolderName) {
  base::HistogramTester histogram_tester;

  payments_client()->ShowSaveCreditCardToCloud(
      test::GetIncompleteCreditCard(), LegalMessageLines(),
      payments::PaymentsAutofillClient::SaveCreditCardOptions()
          .with_should_request_name_from_user(true)
          .with_num_strikes(0)
          .with_show_prompt(true),
      base::DoNothing());
  EXPECT_FALSE([autofill_commands() showSaveCardBottomSheetCalled]);

  histogram_tester.ExpectUniqueSample(
      "Autofill.SaveCreditCardPromptResult.IOS.Server.BottomSheet.NumStrikes.0."
      "RequestingCardHolderName.SavingWithoutCvc",
      autofill_metrics::SaveCreditCardPromptResultIOS::kNotShown,
      /*expected_count=*/1);
}

TEST_F(IOSChromePaymentsAutofillClientTest,
       DoNoShowSaveCardBottomSheet_ForRequestingExpiryDate) {
  base::HistogramTester histogram_tester;

  payments_client()->ShowSaveCreditCardToCloud(
      test::GetExpiredCreditCard(), LegalMessageLines(),
      payments::PaymentsAutofillClient::SaveCreditCardOptions()
          .with_should_request_expiration_date_from_user(true)
          .with_num_strikes(0)
          .with_show_prompt(true),
      base::DoNothing());
  EXPECT_FALSE([autofill_commands() showSaveCardBottomSheetCalled]);

  histogram_tester.ExpectUniqueSample(
      "Autofill.SaveCreditCardPromptResult.IOS.Server.BottomSheet.NumStrikes.0."
      "RequestingExpiryDate.SavingWithoutCvc",
      autofill_metrics::SaveCreditCardPromptResultIOS::kNotShown,
      /*expected_count=*/1);
}

TEST_F(IOSChromePaymentsAutofillClientTest,
       DoNoShowSaveCardBottomSheet_ForRequestingCardHolderNameAndExpiryDate) {
  base::HistogramTester histogram_tester;

  // Passing an empty CreditCard() to `ShowSaveCreditCardToCloud`
  // since this test is regarding missing cardholder name and expiry date.
  payments_client()->ShowSaveCreditCardToCloud(
      CreditCard(), LegalMessageLines(),
      payments::PaymentsAutofillClient::SaveCreditCardOptions()
          .with_should_request_name_from_user(true)
          .with_should_request_expiration_date_from_user(true)
          .with_num_strikes(0)
          .with_show_prompt(true),
      base::DoNothing());
  EXPECT_FALSE([autofill_commands() showSaveCardBottomSheetCalled]);

  histogram_tester.ExpectUniqueSample(
      "Autofill.SaveCreditCardPromptResult.IOS.Server.BottomSheet.NumStrikes.0."
      "RequestingCardHolderNameAndExpiryDate.SavingWithoutCvc",
      autofill_metrics::SaveCreditCardPromptResultIOS::kNotShown,
      /*expected_count=*/1);
}

// Test that on save card success, the save card bottomsheet model's state is
// set to kSaved.
TEST_F(IOSChromePaymentsAutofillClientTest,
       CreditCardUploadCompleted_CardSaved) {
  payments_client()->ShowSaveCreditCardToCloud(
      test::GetCreditCard(), LegalMessageLines(),
      payments::PaymentsAutofillClient::SaveCreditCardOptions()
          .with_num_strikes(0)
          .with_show_prompt(true),
      base::DoNothing());
  payments_client()->CreditCardUploadCompleted(
      /*result=*/payments::PaymentsAutofillClient::PaymentsRpcResult::kSuccess,
      /*on_confirmation_closed_callback=*/std::nullopt);
  EXPECT_EQ((bottomsheet_tab_helper_->GetSaveCardBottomSheetModel())
                ->save_card_state(),
            SaveCardBottomSheetModel::SaveCardState::kSaved);
}

// Test that on save card failure, the save card bottomsheet model's state is
// set to kFailed and the error dialog is shown.
TEST_F(IOSChromePaymentsAutofillClientTest,
       CreditCardUploadCompleted_CardNotSaved) {
  payments_client()->ShowSaveCreditCardToCloud(
      test::GetCreditCard(), LegalMessageLines(),
      payments::PaymentsAutofillClient::SaveCreditCardOptions()
          .with_num_strikes(0)
          .with_show_prompt(true),
      base::DoNothing());
  payments_client()->CreditCardUploadCompleted(
      /*result=*/payments::PaymentsAutofillClient::PaymentsRpcResult::
          kPermanentFailure,
      /*on_confirmation_closed_callback=*/std::nullopt);
  EXPECT_EQ((bottomsheet_tab_helper_->GetSaveCardBottomSheetModel())
                ->save_card_state(),
            SaveCardBottomSheetModel::SaveCardState::kFailed);
  const std::optional<AutofillErrorDialogContext>& error_context =
      [autofill_commands() autofillErrorDialogContext];
  EXPECT_TRUE(error_context.has_value());
  EXPECT_EQ(error_context.value().type,
            AutofillErrorDialogType::kCreditCardUploadError);
}

// Test that on getting client-side timeout, the save card bottomsheet model's
// state is set to kFailed and the error dialog is not shown.
TEST_F(IOSChromePaymentsAutofillClientTest,
       CreditCardUploadCompleted_ClientSideTimeout_NoErrorConfirmation) {
  payments_client()->ShowSaveCreditCardToCloud(
      test::GetCreditCard(), LegalMessageLines(),
      payments::PaymentsAutofillClient::SaveCreditCardOptions()
          .with_num_strikes(0)
          .with_show_prompt(true),
      base::DoNothing());
  payments_client()->CreditCardUploadCompleted(
      /*result=*/payments::PaymentsAutofillClient::PaymentsRpcResult::
          kClientSideTimeout,
      /*on_confirmation_closed_callback=*/std::nullopt);
  EXPECT_EQ((bottomsheet_tab_helper_->GetSaveCardBottomSheetModel())
                ->save_card_state(),
            SaveCardBottomSheetModel::SaveCardState::kFailed);
  const std::optional<AutofillErrorDialogContext>& error_context =
      [autofill_commands() autofillErrorDialogContext];
  EXPECT_FALSE(error_context.has_value());
}

TEST_F(IOSChromePaymentsAutofillClientTest,
       DoesNotLogNotShownForCvcOnlySave_WhenBottomSheetNotShown) {
  base::HistogramTester histogram_tester;

  payments::PaymentsAutofillClient::SaveCreditCardOptions options;
  options.card_save_type =
      payments::PaymentsAutofillClient::CardSaveType::kCvcSaveOnly;
  options.show_prompt = true;
  options.num_strikes = 1;

  payments_client()->ShowSaveCreditCardToCloud(
      test::GetCreditCard(), LegalMessageLines(), std::move(options),
      base::DoNothing());

  // Verify the bottom sheet was not shown.
  EXPECT_FALSE([autofill_commands() showSaveCardBottomSheetCalled]);

  // Verify that the kNotShown metric is logged with the correct CVC suffix.
  histogram_tester.ExpectUniqueSample(
      "Autofill.SaveCreditCardPromptResult.IOS.Server.BottomSheet.NumStrikes.1."
      "NoFixFlow.SavingWithCvc",
      autofill_metrics::SaveCreditCardPromptResultIOS::kNotShown,
      /*expected_count=*/0);
}

TEST_F(IOSChromePaymentsAutofillClientTest,
       LogsNotShownForCardSaveWithCvc_WhenBottomSheetNotShown) {
  base::HistogramTester histogram_tester;

  payments::PaymentsAutofillClient::SaveCreditCardOptions options;
  options.card_save_type =
      payments::PaymentsAutofillClient::CardSaveType::kCardSaveWithCvc;
  options.show_prompt = true;
  options.num_strikes = 1;

  payments_client()->ShowSaveCreditCardToCloud(
      test::GetCreditCard(), LegalMessageLines(), std::move(options),
      base::DoNothing());

  // Verify the bottom sheet was not shown.
  EXPECT_FALSE([autofill_commands() showSaveCardBottomSheetCalled]);

  // Verify that the kNotShown metric is logged with the correct CVC suffix.
  histogram_tester.ExpectUniqueSample(
      "Autofill.SaveCreditCardPromptResult.IOS.Server.BottomSheet.NumStrikes.1."
      "NoFixFlow.SavingWithCvc",
      autofill_metrics::SaveCreditCardPromptResultIOS::kNotShown,
      /*expected_count=*/1);
}

TEST_F(IOSChromePaymentsAutofillClientTest,
       ShowSaveCardBottomSheet_WithLocalCard) {
  payments_client()->ShowSaveCreditCardLocally(
      test::GetCreditCard(),
      payments::PaymentsAutofillClient::SaveCreditCardOptions()
          .with_num_strikes(0)
          .with_show_prompt(true),
      base::DoNothing());
  EXPECT_TRUE([autofill_commands() showSaveCardBottomSheetCalled]);
}

TEST_F(IOSChromePaymentsAutofillClientTest,
       DoNoShowSaveCardBottomSheet_LocalCardWithMoreThan0Strike) {
  base::HistogramTester histogram_tester;

  payments_client()->ShowSaveCreditCardLocally(
      test::GetCreditCard(),
      payments::PaymentsAutofillClient::SaveCreditCardOptions()
          .with_num_strikes(1)
          .with_show_prompt(true),
      base::DoNothing());
  EXPECT_FALSE([autofill_commands() showSaveCardBottomSheetCalled]);

  histogram_tester.ExpectUniqueSample(
      "Autofill.SaveCreditCardPromptResult.IOS.Local.BottomSheet.NumStrikes.1."
      "NoFixFlow.SavingWithoutCvc",
      autofill_metrics::SaveCreditCardPromptResultIOS::kNotShown,
      /*expected_count=*/1);
}

TEST_F(IOSChromePaymentsAutofillClientTest,
       ShowSaveCardBottomSheet_LocalCardWithMissingCardHolderName) {
  base::HistogramTester histogram_tester;

  payments_client()->ShowSaveCreditCardLocally(
      test::GetIncompleteCreditCard(),
      payments::PaymentsAutofillClient::SaveCreditCardOptions()
          .with_should_request_name_from_user(true)
          .with_num_strikes(0)
          .with_show_prompt(true),
      base::DoNothing());
  EXPECT_TRUE([autofill_commands() showSaveCardBottomSheetCalled]);

  histogram_tester.ExpectUniqueSample(
      "Autofill.SaveCreditCardPromptResult.IOS.Local.BottomSheet.NumStrikes.0."
      "RequestingCardHolderName.SavingWithoutCvc",
      autofill_metrics::SaveCreditCardPromptResultIOS::kNotShown,
      /*expected_count=*/0);
}

TEST_F(IOSChromePaymentsAutofillClientTest,
       ShowSaveCardBottomSheet_LocalCardWithInvalidExpiryDate) {
  base::HistogramTester histogram_tester;

  payments_client()->ShowSaveCreditCardLocally(
      test::GetExpiredCreditCard(),
      payments::PaymentsAutofillClient::SaveCreditCardOptions()
          .with_should_request_expiration_date_from_user(true)
          .with_num_strikes(0)
          .with_show_prompt(true),
      base::DoNothing());
  EXPECT_TRUE([autofill_commands() showSaveCardBottomSheetCalled]);

  histogram_tester.ExpectUniqueSample(
      "Autofill.SaveCreditCardPromptResult.IOS.Local.BottomSheet.NumStrikes.0."
      "RequestingExpiryDate.SavingWithoutCvc",
      autofill_metrics::SaveCreditCardPromptResultIOS::kNotShown,
      /*expected_count=*/0);
}

TEST_F(
    IOSChromePaymentsAutofillClientTest,
    ShowSaveCardBottomSheet_LocalCardWithMissingCardHolderNameAndInvalidExpiryDate) {
  base::HistogramTester histogram_tester;
  CreditCard card = test::GetIncompleteCreditCard();
  card.SetExpirationMonth(1);
  card.SetExpirationYear(2020);
  payments_client()->ShowSaveCreditCardLocally(
      card,
      payments::PaymentsAutofillClient::SaveCreditCardOptions()
          .with_should_request_name_from_user(true)
          .with_should_request_expiration_date_from_user(true)
          .with_num_strikes(0)
          .with_show_prompt(true),
      base::DoNothing());
  EXPECT_TRUE([autofill_commands() showSaveCardBottomSheetCalled]);

  histogram_tester.ExpectUniqueSample(
      "Autofill.SaveCreditCardPromptResult.IOS.Local.BottomSheet.NumStrikes.0."
      "RequestingCardHolderNameAndExpiryDate.SavingWithoutCvc",
      autofill_metrics::SaveCreditCardPromptResultIOS::kNotShown,
      /*expected_count=*/0);
}

// Tests that ShowCreditCardLocalSaveAndFillDialog shows the save card bottom
// sheet with the kScanCardSaveAndFill source feature.
TEST_F(IOSChromePaymentsAutofillClientTest,
       ShowCreditCardLocalSaveAndFillDialog_ShowsBottomSheet) {
  payments_client()->ShowCreditCardLocalSaveAndFillDialog(base::DoNothing());

  // Verify that the bottom sheet command was invoked.
  EXPECT_TRUE([autofill_commands() showSaveCardBottomSheetCalled]);

  // Retrieve the model from the tab helper to verify options.
  std::unique_ptr<SaveCardBottomSheetModel> model =
      bottomsheet_tab_helper_->GetSaveCardBottomSheetModel();
  ASSERT_TRUE(model);

  // Verify that the SourceFeature is set to kScanCardSaveAndFill.
  EXPECT_EQ(
      model->save_card_delegate()->GetSaveCreditCardOptions().source_feature,
      payments::PaymentsAutofillClient::SourceFeature::kScanCardSaveAndFill);
}

// Tests that HideCreditCardSaveAndFillDialog marks local save bottom sheet as
// completed.
TEST_F(IOSChromePaymentsAutofillClientTest,
       HideCreditCardSaveAndFillDialog_LocalSaveBottomSheetCompletes) {
  payments_client()->ShowCreditCardLocalSaveAndFillDialog(base::DoNothing());
  EXPECT_TRUE([autofill_commands() showSaveCardBottomSheetCalled]);

  payments_client()->HideCreditCardSaveAndFillDialog();

  std::unique_ptr<SaveCardBottomSheetModel> model =
      bottomsheet_tab_helper_->GetSaveCardBottomSheetModel();
  ASSERT_TRUE(model);
  EXPECT_EQ(model->save_card_state(),
            SaveCardBottomSheetModel::SaveCardState::kFailed);
}

// Tests that OnCardDataAvailable populates the ManualFillVirtualCardCache.
TEST_F(IOSChromePaymentsAutofillClientTest,
       OnCardDataAvailable_CachesVirtualCard) {
  // Create a virtual card
  CreditCard card = test::GetVirtualCard();
  card.set_server_id("test_server_id");
  card.set_record_type(CreditCard::RecordType::kVirtualCard);

  FilledCardInformationBubbleOptions options;
  options.filled_card = card;
  options.cvc = u"123";

  url::Origin test_origin = url::Origin::Create(GURL("https://example.com"));
  ManualFillVirtualCardCache::CreateForWebState(web_state_.get());

  payments_client()->OnCardDataAvailable(options, test_origin);

  ManualFillVirtualCardCache* cache =
      ManualFillVirtualCardCache::FromWebState(web_state_.get());
  ASSERT_TRUE(cache);

  const CreditCard* cached_card =
      cache->GetUnmaskedCard(card.server_id(), test_origin);
  ASSERT_TRUE(cached_card);
  EXPECT_EQ(cached_card->cvc(), u"123");
}

// Tests that IsAutofillPaymentMethodsEnabled returns true when a domain
// is not blocked by enterprise policy, and false when blocked.
TEST_F(IOSChromePaymentsAutofillClientTest, IsAutofillPaymentMethodsEnabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(
      features::kAutofillEnableAutofillSettingsEnterprisePolicy);

  // Default is enabled (not blocked).
  EXPECT_TRUE(payments_client()->IsAutofillPaymentMethodsEnabled());

  // Block the domain for payments category.
  base::ListValue blocked_list;
  base::DictValue entry;
  entry.Set("url_pattern", "https://[*.]example.com");
  base::ListValue blocked_types;
  blocked_types.Append("payments");
  entry.Set("blocked_types", std::move(blocked_types));
  blocked_list.Append(std::move(entry));
  profile_->GetPrefs()->SetList(prefs::kAutofillTypesBlocked,
                                std::move(blocked_list));

  // Navigate to blocked domain.
  client()->set_last_committed_primary_main_frame_url(
      GURL("https://www.example.com"));

  EXPECT_FALSE(payments_client()->IsAutofillPaymentMethodsEnabled());

  // Navigate to unblocked domain.
  client()->set_last_committed_primary_main_frame_url(
      GURL("https://www.google.com"));

  EXPECT_TRUE(payments_client()->IsAutofillPaymentMethodsEnabled());
}

TEST_F(IOSChromePaymentsAutofillClientTest,
       GetWalletReminderNoticeUiDelegate_ReturnsNonNull) {
  EXPECT_NE(payments_client()->GetWalletReminderNoticeUiDelegate(), nullptr);
}

TEST_F(IOSChromePaymentsAutofillClientTest,
       GetWalletReminderNoticeManager_ReturnsNonNull) {
  EXPECT_NE(payments_client()->GetWalletReminderNoticeManager(), nullptr);
}

}  // namespace

}  // namespace autofill
