// Copyright 2018 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/manual_fill/model/manual_fill_full_card_requester.h"

#import <memory>
#import <vector>

#import "base/functional/callback_helpers.h"
#import "base/memory/weak_ptr.h"
#import "components/autofill/core/browser/data_model/payments/credit_card.h"
#import "components/autofill/core/browser/foundations/browser_autofill_manager.h"
#import "components/autofill/core/browser/payments/credit_card_access_manager.h"
#import "components/autofill/ios/browser/autofill_driver_ios.h"
#import "components/autofill/ios/browser/autofill_java_script_feature.h"
#import "ios/chrome/browser/autofill/manual_fill/model/full_card_request_result_delegate_bridge.h"
#import "ios/chrome/browser/autofill/manual_fill/public/manual_fill_constants.h"
#import "ios/chrome/browser/autofill/model/manual_fill_virtual_card_cache.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
#import "ios/web/public/js_messaging/web_frame.h"
#import "ios/web/public/js_messaging/web_frames_manager.h"
#import "ios/web/public/web_state.h"

using autofill::CreditCard::RecordType::kVirtualCard;

namespace autofill {
class CreditCard;
}  // namespace autofill

@interface ManualFillFullCardRequester ()

// The ProfileIOS instance passed to the initializer.
@property(nonatomic, readonly) ProfileIOS* profile;

// The WebStateList for this instance. Used to instantiate the child
// coordinators lazily.
@property(nonatomic, readonly) WebStateList* webStateList;

@end

@implementation ManualFillFullCardRequester {
  // Obj-C delegate to receive the success or failure result, when
  // asking credit card unlocking.
  __weak id<FullCardRequestResultDelegateObserving> _delegate;
}

- (instancetype)initWithProfile:(ProfileIOS*)profile
                   webStateList:(WebStateList*)webStateList
                 resultDelegate:
                     (id<FullCardRequestResultDelegateObserving>)delegate {
  self = [super init];
  if (self) {
    _profile = profile;
    _webStateList = webStateList;
    _delegate = delegate;
  }
  return self;
}

- (void)requestFullCreditCard:(const autofill::CreditCard)card
       withBaseViewController:(UIViewController*)viewController
                   recordType:(autofill::CreditCard::RecordType)recordType
                    fieldType:(manual_fill::PaymentFieldType)fieldType
                       origin:(url::Origin)origin {
  // Payment Request is only enabled in main frame.
  web::WebState* webState = self.webStateList->GetActiveWebState();
  web::WebFramesManager* frames_manager =
      autofill::AutofillJavaScriptFeature::GetInstance()->GetWebFramesManager(
          webState);
  web::WebFrame* mainFrame = frames_manager->GetMainWebFrame();
  if (!mainFrame) {
    return;
  }
  autofill::BrowserAutofillManager& autofillManager =
      autofill::AutofillDriverIOS::FromWebStateAndWebFrame(webState, mainFrame)
          ->GetAutofillManager();

  autofill::CreditCard virtualCard;
  if (recordType == kVirtualCard) {
    virtualCard = autofill::CreditCard::CreateVirtualCard(card);
  }
  autofill::CreditCardAccessManager* creditCardAccessManager =
      autofillManager.GetCreditCardAccessManager();
  __weak __typeof(self) weakSelf = self;

  BOOL isVirtualCard = (recordType == kVirtualCard);
  creditCardAccessManager->FetchCreditCard(
      (isVirtualCard ? &virtualCard : &card),
      base::BindOnce(
          [](__weak ManualFillFullCardRequester* weak_self,
             manual_fill::PaymentFieldType field_type, BOOL is_virtual_card,
             url::Origin origin, base::WeakPtr<web::WebState> web_state,
             const autofill::CreditCard& fetched_card) {
            autofill::CreditCard result_card = fetched_card;
            // The `CreditCardAccessManager` returns a card with record type
            // `kFullServerCard` for unmasked cards. We need to force it back to
            // `kVirtualCard` so that the iOS manual fill flow can recognize it
            // correctly.
            if (is_virtual_card) {
              result_card.set_record_type(
                  autofill::CreditCard::RecordType::kVirtualCard);
            }
            [weak_self onCreditCardFetched:result_card
                                 fieldType:field_type
                                    origin:std::move(origin)
                                  webState:web_state];
          },
          weakSelf, fieldType, isVirtualCard, std::move(origin),
          webState->GetWeakPtr()));

  // TODO(crbug.com/40577448): closing CVC requester doesn't restore icon bar
  // above keyboard.
}

#pragma mark - Private methods

// Callback invoked when the card retrieval is finished. It notifies the
// delegate the result of the card retrieval process and provides the card if
// the process succeeded.
- (void)onCreditCardFetched:(const autofill::CreditCard&)fetchedCard
                  fieldType:(manual_fill::PaymentFieldType)fieldType
                     origin:(url::Origin)origin
                   webState:(base::WeakPtr<web::WebState>)webState {
  if (!webState) {
    return;
  }
  if (fetchedCard.record_type() == kVirtualCard) {
    ManualFillVirtualCardCache::CreateForWebState(webState.get());
    ManualFillVirtualCardCache::FromWebState(webState.get())
        ->CacheUnmaskedCard(fetchedCard, std::move(origin));
  }
  [_delegate onFullCardRequestSucceeded:fetchedCard
                              fieldType:fieldType
                            forWebState:webState.get()];
}

@end
