// 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 <memory>

#import "base/functional/bind.h"
#import "base/strings/sys_string_conversions.h"
#import "components/autofill/core/browser/payments/legal_message_line.h"
#import "components/autofill/core/browser/payments/payments_autofill_client.h"
#import "ios/web/public/thread/web_task_traits.h"
#import "ios/web/public/thread/web_thread.h"
#import "ios/web_view/internal/autofill/cwv_autofill_util.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 "net/base/apple/url_conversions.h"
#import "ui/gfx/range/range.h"

NS_ASSUME_NONNULL_BEGIN

@implementation CWVCreditCardSaver {
  autofill::payments::PaymentsAutofillClient::SaveCreditCardOptions
      _saveOptions;
  autofill::payments::PaymentsAutofillClient::UploadSaveCardPromptCallback
      _saveCardCallback;

  // The callback to invoke for save completion results.
  void (^_Nullable _saveCompletionHandler)(BOOL);

  // The callback to invoke for returning risk data.
  base::OnceCallback<void(const std::string&)> _riskDataCallback;

  // Whether or not either |acceptCreditCardWithRiskData:completionHandler:| or
  // |declineCreditCardSave| has been called.
  BOOL _decisionMade;
}

@synthesize creditCard = _creditCard;
@synthesize legalMessages = _legalMessages;

- (instancetype)
    initWithCreditCard:(const autofill::CreditCard&)creditCard
           saveOptions:(autofill::payments::PaymentsAutofillClient::
                            SaveCreditCardOptions)saveOptions
     legalMessageLines:(autofill::LegalMessageLines)legalMessageLines
    savePromptCallback:(autofill::payments::PaymentsAutofillClient::
                            UploadSaveCardPromptCallback)savePromptCallback {
  self = [super init];
  if (self) {
    _creditCard = [[CWVCreditCard alloc] initWithCreditCard:creditCard];
    _saveOptions = saveOptions;
    _legalMessages = CWVLegalMessagesFromLegalMessageLines(legalMessageLines);
    _saveCardCallback = std::move(savePromptCallback);
    _decisionMade = NO;
  }
  return self;
}

- (void)dealloc {
  // If the user did not choose, the decision should be marked as ignored.
  if (_saveCardCallback) {
    std::move(_saveCardCallback)
        .Run(autofill::payments::PaymentsAutofillClient::
                 SaveCardOfferUserDecision::kIgnored,
             /*user_provided_card_details=*/{});
  }
}

#pragma mark - Public Methods

- (void)acceptWithCardHolderFullName:(NSString*)cardHolderFullName
                     expirationMonth:(NSString*)expirationMonth
                      expirationYear:(NSString*)expirationYear
                            riskData:(NSString*)riskData
                   completionHandler:(void (^)(BOOL))completionHandler {
  DCHECK(!_decisionMade)
      << "You may only call -acceptWithRiskData:completionHandler: or "
         "-decline: once per instance.";
  DCHECK(_riskDataCallback && riskData);
  std::move(_riskDataCallback).Run(base::SysNSStringToUTF8(riskData));

  _saveCompletionHandler = completionHandler;
  DCHECK(_saveCardCallback);
  autofill::payments::PaymentsAutofillClient::UserProvidedCardDetails details;
  details.cardholder_name = base::SysNSStringToUTF16(cardHolderFullName);
  details.expiration_date_month = base::SysNSStringToUTF16(expirationMonth);
  details.expiration_date_year = base::SysNSStringToUTF16(expirationYear);
  std::move(_saveCardCallback)
      .Run(autofill::payments::PaymentsAutofillClient::
               SaveCardOfferUserDecision::kAccepted,
           details);
  _decisionMade = YES;
}

- (void)decline {
  DCHECK(!_decisionMade)
      << "You may only call -acceptWithRiskData:completionHandler: or "
         "-decline: once per instance.";
  DCHECK(_saveCardCallback);
  std::move(_saveCardCallback)
      .Run(autofill::payments::PaymentsAutofillClient::
               SaveCardOfferUserDecision::kDeclined,
           /*user_provided_card_details=*/{});
  _decisionMade = YES;
}

#pragma mark - Internal Methods

- (void)handleCreditCardUploadCompleted:(BOOL)cardSaved {
  if (_saveCompletionHandler) {
    _saveCompletionHandler(cardSaved);
    _saveCompletionHandler = nil;
  }
}

- (void)loadRiskData:(base::OnceCallback<void(const std::string&)>)callback {
  _riskDataCallback = std::move(callback);
}

@end

NS_ASSUME_NONNULL_END
