// 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 <UIKit/UIKit.h>

#import <memory>

#import "base/functional/bind.h"
#import "base/run_loop.h"
#import "base/test/bind.h"
#import "base/test/ios/wait_util.h"
#import "components/autofill/core/browser/payments/legal_message_line.h"
#import "components/autofill/core/browser/payments/payments_autofill_client.h"
#import "components/autofill/core/browser/payments/test_legal_message_line.h"
#import "components/autofill/core/browser/test_utils/autofill_test_utils.h"
#import "ios/web/public/test/web_task_environment.h"
#import "ios/web_view/internal/autofill/cwv_credit_card_internal.h"
#import "ios/web_view/internal/autofill/cwv_credit_card_saver_internal.h"
#import "ios/web_view/test/test_with_locale_and_resources.h"
#import "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"

using base::test::ios::kWaitForActionTimeout;
using base::test::ios::WaitUntilConditionOrTimeout;

namespace ios_web_view {
class CWVCreditCardSaverTest : public TestWithLocaleAndResources {
 protected:
  CWVCreditCardSaverTest() {}

  web::WebTaskEnvironment task_environment_;
};

// Tests CWVCreditCardSaver properly initializes.
TEST_F(CWVCreditCardSaverTest, Initialization) {
  autofill::CreditCard credit_card = autofill::test::GetCreditCard();
  autofill::payments::PaymentsAutofillClient::SaveCreditCardOptions options;
  autofill::LegalMessageLines legal_message_lines = {
      autofill::TestLegalMessageLine("Test line 1",
                                     {autofill::LegalMessageLine::Link(
                                         5, 9, "http://www.chromium.org/")})};
  autofill::payments::PaymentsAutofillClient::UploadSaveCardPromptCallback
      callback;

  CWVCreditCardSaver* credit_card_saver =
      [[CWVCreditCardSaver alloc] initWithCreditCard:credit_card
                                         saveOptions:options
                                   legalMessageLines:legal_message_lines
                                  savePromptCallback:std::move(callback)];

  EXPECT_EQ(credit_card, *credit_card_saver.creditCard.internalCard);
  ASSERT_EQ(1U, credit_card_saver.legalMessages.count);
  NSAttributedString* legal_message =
      credit_card_saver.legalMessages.firstObject;
  EXPECT_NSEQ(@"Test line 1", legal_message.string);
  NSRange range;
  id link = [legal_message attribute:NSLinkAttributeName
                             atIndex:5
                      effectiveRange:&range];
  EXPECT_NSEQ([NSURL URLWithString:@"http://www.chromium.org/"], link);
  EXPECT_TRUE(NSEqualRanges(NSMakeRange(5, 4), range));
}

// Tests when user ignores credit card save.
TEST_F(CWVCreditCardSaverTest, Ignore) {
  autofill::CreditCard credit_card = autofill::test::GetCreditCard();
  autofill::payments::PaymentsAutofillClient::SaveCreditCardOptions options;

  BOOL callback_called = NO;
  autofill::payments::PaymentsAutofillClient::UploadSaveCardPromptCallback
      callback = base::BindLambdaForTesting(
          [&](autofill::payments::PaymentsAutofillClient::
                  SaveCardOfferUserDecision decision,
              const autofill::payments::PaymentsAutofillClient::
                  UserProvidedCardDetails& user_provided_card_details) {
            callback_called = YES;
            EXPECT_EQ(autofill::payments::PaymentsAutofillClient::
                          SaveCardOfferUserDecision::kIgnored,
                      decision);
          });

  [[maybe_unused]] CWVCreditCardSaver* credit_card_saver =
      [[CWVCreditCardSaver alloc] initWithCreditCard:credit_card
                                         saveOptions:options
                                   legalMessageLines:{}
                                  savePromptCallback:std::move(callback)];
  // Force -[CWVCreditCardSaver dealloc].
  credit_card_saver = nil;

  EXPECT_TRUE(callback_called);
}

// Tests when user declines a save.
TEST_F(CWVCreditCardSaverTest, Decline) {
  autofill::CreditCard credit_card = autofill::test::GetCreditCard();
  autofill::payments::PaymentsAutofillClient::SaveCreditCardOptions options;
  autofill::payments::PaymentsAutofillClient::LocalSaveCardPromptCallback
      local_callback;

  BOOL callback_called = NO;
  autofill::payments::PaymentsAutofillClient::UploadSaveCardPromptCallback
      callback = base::BindLambdaForTesting(
          [&](autofill::payments::PaymentsAutofillClient::
                  SaveCardOfferUserDecision decision,
              const autofill::payments::PaymentsAutofillClient::
                  UserProvidedCardDetails& user_provided_card_details) {
            callback_called = YES;
            EXPECT_EQ(autofill::payments::PaymentsAutofillClient::
                          SaveCardOfferUserDecision::kDeclined,
                      decision);
          });

  CWVCreditCardSaver* credit_card_saver =
      [[CWVCreditCardSaver alloc] initWithCreditCard:credit_card
                                         saveOptions:options
                                   legalMessageLines:{}
                                  savePromptCallback:std::move(callback)];
  [credit_card_saver decline];

  EXPECT_TRUE(callback_called);
}

// Tests when user accepts a save.
TEST_F(CWVCreditCardSaverTest, Accept) {
  autofill::CreditCard credit_card = autofill::test::GetCreditCard();
  autofill::payments::PaymentsAutofillClient::SaveCreditCardOptions options;

  BOOL callback_called = NO;
  autofill::payments::PaymentsAutofillClient::UploadSaveCardPromptCallback
      callback = base::BindLambdaForTesting(
          [&](autofill::payments::PaymentsAutofillClient::
                  SaveCardOfferUserDecision decision,
              const autofill::payments::PaymentsAutofillClient::
                  UserProvidedCardDetails& user_provided_card_details) {
            callback_called = YES;
            EXPECT_EQ(autofill::payments::PaymentsAutofillClient::
                          SaveCardOfferUserDecision::kAccepted,
                      decision);
            EXPECT_EQ(u"John Doe", user_provided_card_details.cardholder_name);
            EXPECT_EQ(u"08", user_provided_card_details.expiration_date_month);
            EXPECT_EQ(u"2021", user_provided_card_details.expiration_date_year);
          });

  CWVCreditCardSaver* credit_card_saver =
      [[CWVCreditCardSaver alloc] initWithCreditCard:credit_card
                                         saveOptions:options
                                   legalMessageLines:{}
                                  savePromptCallback:std::move(callback)];
  BOOL risk_data_used = NO;
  [credit_card_saver loadRiskData:base::BindLambdaForTesting(
                                      [&](const std::string& risk_data) {
                                        EXPECT_EQ(risk_data, "dummy-risk-data");
                                        risk_data_used = YES;
                                      })];
  __block BOOL completion_called = NO;
  [credit_card_saver acceptWithCardHolderFullName:@"John Doe"
                                  expirationMonth:@"08"
                                   expirationYear:@"2021"
                                         riskData:@"dummy-risk-data"
                                completionHandler:^(BOOL cardSaved) {
                                  EXPECT_TRUE(cardSaved);
                                  completion_called = YES;
                                }];
  [credit_card_saver handleCreditCardUploadCompleted:YES];

  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForActionTimeout, ^bool {
    base::RunLoop().RunUntilIdle();
    return completion_called;
  }));

  EXPECT_TRUE(risk_data_used);
  EXPECT_TRUE(callback_called);
}

}  // namespace ios_web_view
