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

#include "chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.h"

#include <concepts>
#include <memory>

#include "chrome/browser/ui/actions/chrome_action_id.h"
#include "chrome/browser/ui/autofill/autofill_ai/autofill_ai_import_data_controller.h"
#include "chrome/browser/ui/autofill/autofill_bubble_base.h"
#include "chrome/browser/ui/autofill/payments/payments_churned_users_bubble_controller.h"
#include "chrome/browser/ui/autofill/payments/save_card_ui.h"
#include "chrome/browser/ui/autofill/payments/save_iban_ui.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_element_identifiers.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/views/autofill/address_sign_in_promo_view.h"
#include "chrome/browser/ui/views/autofill/autofill_ai/autofill_ai_import_data_bubble_view.h"
#include "chrome/browser/ui/views/autofill/autofill_ai/autofill_ai_local_save_notification_view.h"
#include "chrome/browser/ui/views/autofill/payments/filled_card_information_bubble_views.h"
#include "chrome/browser/ui/views/autofill/payments/manage_saved_iban_bubble_view.h"
#include "chrome/browser/ui/views/autofill/payments/mandatory_reauth_confirmation_bubble_view.h"
#include "chrome/browser/ui/views/autofill/payments/mandatory_reauth_opt_in_bubble_view.h"
#include "chrome/browser/ui/views/autofill/payments/offer_notification_bubble_views.h"
#include "chrome/browser/ui/views/autofill/payments/omnibox_autofill_bubble_view.h"
#include "chrome/browser/ui/views/autofill/payments/payments_churned_users_bubble_view.h"
#include "chrome/browser/ui/views/autofill/payments/save_card_bubble_views.h"
#include "chrome/browser/ui/views/autofill/payments/save_card_manage_cards_bubble_views.h"
#include "chrome/browser/ui/views/autofill/payments/save_card_offer_bubble_views.h"
#include "chrome/browser/ui/views/autofill/payments/save_iban_bubble_view.h"
#include "chrome/browser/ui/views/autofill/payments/save_payment_method_and_virtual_card_enroll_confirmation_bubble_views.h"
#include "chrome/browser/ui/views/autofill/payments/virtual_card_enroll_bubble_views.h"
#include "chrome/browser/ui/views/autofill/payments/wallet_reminder_notice_bubble_view.h"
#include "chrome/browser/ui/views/autofill/save_address_profile_view.h"
#include "chrome/browser/ui/views/autofill/update_address_profile_view.h"
#include "chrome/browser/ui/views/bubble_anchor_util_views.h"
#include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
#include "components/autofill/core/browser/foundations/autofill_client.h"
#include "components/autofill/core/browser/payments/virtual_card_enrollment_manager.h"
#include "components/autofill/core/browser/ui/payments/virtual_card_enroll_bubble_controller.h"
#include "components/constrained_window/constrained_window_views.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"

namespace autofill {

namespace {

template <typename View, typename... Args>
  requires(std::derived_from<View, AutofillLocationBarBubble>)
View* ShowBubble(ToolbarButtonProvider* toolbar_button_provider,
                 std::optional<actions::ActionId> action_id,
                 ui::ElementIdentifier highlight_element,
                 bool is_user_gesture,
                 Args&&... args) {
  views::BubbleAnchor const anchor_view =
      toolbar_button_provider->GetBubbleAnchor(action_id);
  auto bubble =
      std::make_unique<View>(anchor_view, std::forward<Args>(args)...);
  // Handle anchors for icon highlighting.
  if (!bubble_anchor_util::IsHighlightable(anchor_view)) {
    bubble->SetHighlightedElement(highlight_element);
  }

  View* const bubble_ptr = bubble.get();
  views::BubbleDialogDelegateView::CreateBubble(std::move(bubble));
  const LocationBarBubbleDelegateView::DisplayReason display_reason =
      is_user_gesture ? LocationBarBubbleDelegateView::USER_GESTURE
                      : LocationBarBubbleDelegateView::AUTOMATIC;
  // TODO(crbug.com/40679714): Check whether the Show methods can be eliminated
  // in favor of just using ShowForReason.
  if constexpr (requires { bubble_ptr->Show(display_reason); }) {
    bubble_ptr->Show(display_reason);
  } else {
    bubble_ptr->ShowForReason(display_reason);
  }
  return bubble_ptr;
}

template <typename View, typename... Args>
  requires std::derived_from<View, AutofillLocationBarBubble>
View* ShowAddressProfileBubble(ToolbarButtonProvider* toolbar_button_provider,
                               bool is_user_gesture,
                               Args&&... args) {
  return ShowBubble<View>(toolbar_button_provider,
                          kActionShowAddressesBubbleOrPage,
                          kAutofillAddressPageActionElementId, is_user_gesture,
                          std::forward<Args>(args)...);
}

}  // namespace

AutofillBubbleHandlerImpl::AutofillBubbleHandlerImpl(
    BrowserWindowInterface* browser,
    ToolbarButtonProvider* toolbar_button_provider)
    : toolbar_button_provider_(toolbar_button_provider),
      scoped_user_data_(browser->GetUnownedUserDataHost(), *this) {}

AutofillBubbleHandlerImpl::~AutofillBubbleHandlerImpl() = default;

AutofillBubbleBase* AutofillBubbleHandlerImpl::ShowSaveCreditCardBubble(
    content::WebContents* web_contents,
    SaveCardBubbleController* controller,
    bool is_user_gesture) {
  switch (controller->GetPaymentsBubbleType()) {
    case PaymentsBubbleType::kLocalSave:
    case PaymentsBubbleType::kLocalCvcSave:
    case PaymentsBubbleType::kUploadSave:
    case PaymentsBubbleType::kUploadCvcSave:
    case PaymentsBubbleType::kUploadInProgress:
      return ShowBubble<SaveCardOfferBubbleViews>(
          toolbar_button_provider_, kActionShowPaymentsBubbleOrPage,
          kAutofillSavePaymentsPageActionElementId, is_user_gesture,
          web_contents, controller);
    case PaymentsBubbleType::kManageCards:
      return ShowBubble<SaveCardManageCardsBubbleViews>(
          toolbar_button_provider_, kActionShowPaymentsBubbleOrPage,
          kAutofillSavePaymentsPageActionElementId, is_user_gesture,
          web_contents, controller);
    case PaymentsBubbleType::kUploadComplete:
    case PaymentsBubbleType::kInactive:
      break;
  }
  NOTREACHED();
}

AutofillBubbleBase* AutofillBubbleHandlerImpl::ShowIbanBubble(
    content::WebContents* web_contents,
    IbanBubbleController* controller,
    bool is_user_gesture,
    IbanBubbleType bubble_type) {
  switch (bubble_type) {
    case IbanBubbleType::kLocalSave:
    case IbanBubbleType::kUploadSave:
    case IbanBubbleType::kUploadInProgress:
      return ShowBubble<SaveIbanBubbleView>(
          toolbar_button_provider_, kActionShowPaymentsBubbleOrPage,
          kAutofillSavePaymentsPageActionElementId, is_user_gesture,
          web_contents, controller);
    case IbanBubbleType::kManageSavedIban:
      return ShowBubble<ManageSavedIbanBubbleView>(
          toolbar_button_provider_, kActionShowPaymentsBubbleOrPage,
          kAutofillSavePaymentsPageActionElementId, is_user_gesture,
          web_contents, controller);
    case IbanBubbleType::kUploadCompleted:
    case IbanBubbleType::kInactive:
      break;
  }
  NOTREACHED();
}

AutofillBubbleBase* AutofillBubbleHandlerImpl::ShowOfferNotificationBubble(
    content::WebContents* web_contents,
    OfferNotificationBubbleController* controller,
    bool is_user_gesture) {
  return ShowBubble<OfferNotificationBubbleViews>(
      toolbar_button_provider_, kActionOffersAndRewardsForPage,
      kOfferNotificationChipElementId, is_user_gesture, web_contents,
      controller);
}

AutofillBubbleBase* AutofillBubbleHandlerImpl::ShowSaveAddressProfileBubble(
    content::WebContents* web_contents,
    std::unique_ptr<SaveAddressBubbleController> controller,
    bool is_user_gesture) {
  return ShowAddressProfileBubble<SaveAddressProfileView>(
      toolbar_button_provider_, is_user_gesture, std::move(controller),
      web_contents);
}

#if BUILDFLAG(ENABLE_DICE_SUPPORT)
AutofillBubbleBase* AutofillBubbleHandlerImpl::ShowAddressSignInPromo(
    content::WebContents* web_contents,
    const AutofillProfile& autofill_profile) {
  views::BubbleAnchor anchor = toolbar_button_provider_->GetBubbleAnchor(
      kActionShowAddressesBubbleOrPage);
  AddressSignInPromoView* bubble =
      new AddressSignInPromoView(anchor, web_contents, autofill_profile);
  if (!bubble_anchor_util::IsHighlightable(anchor)) {
    bubble->SetHighlightedElement(kAutofillAddressPageActionElementId);
  }

  views::BubbleDialogDelegateView::CreateBubble(bubble);
  bubble->ShowForReason(LocationBarBubbleDelegateView::AUTOMATIC);
  return bubble;
}
#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)

AutofillBubbleBase* AutofillBubbleHandlerImpl::ShowSaveAutofillAiDataBubble(
    content::WebContents* web_contents,
    AutofillAiImportDataController* controller) {
  return ShowBubble<AutofillAiImportDataBubbleView>(
      toolbar_button_provider_, kActionShowAddressesBubbleOrPage,
      kAutofillAddressPageActionElementId, /*is_user_gesture=*/false,
      web_contents, controller);
}

AutofillBubbleBase*
AutofillBubbleHandlerImpl::ShowAutofillAiLocalSaveNotification(
    content::WebContents* web_contents,
    AutofillAiImportDataController* controller) {
  return ShowBubble<AutofillAiLocalSaveNotificationView>(
      toolbar_button_provider_, kActionShowAddressesBubbleOrPage,
      kAutofillAddressPageActionElementId, /*is_user_gesture=*/false,
      web_contents, controller);
}

AutofillBubbleBase* AutofillBubbleHandlerImpl::ShowUpdateAddressProfileBubble(
    content::WebContents* web_contents,
    std::unique_ptr<UpdateAddressBubbleController> controller,
    bool is_user_gesture) {
  return ShowAddressProfileBubble<UpdateAddressProfileView>(
      toolbar_button_provider_, is_user_gesture, std::move(controller),
      web_contents);
}

AutofillBubbleBase* AutofillBubbleHandlerImpl::ShowFilledCardInformationBubble(
    content::WebContents* web_contents,
    FilledCardInformationBubbleController* controller,
    bool is_user_gesture) {
  return ShowBubble<FilledCardInformationBubbleViews>(
      toolbar_button_provider_, kActionFilledCardInformation,
      kAutofillFilledCardInformationPageActionElementId, is_user_gesture,
      web_contents, controller);
}

AutofillBubbleBase* AutofillBubbleHandlerImpl::ShowVirtualCardEnrollBubble(
    content::WebContents* web_contents,
    VirtualCardEnrollBubbleController* controller,
    bool is_user_gesture) {
  views::BubbleAnchor anchor =
      toolbar_button_provider_->GetBubbleAnchor(kActionVirtualCardEnroll);
  VirtualCardEnrollBubbleViews* bubble =
      new VirtualCardEnrollBubbleViews(anchor, web_contents, controller);

  views::BubbleDialogDelegateView::CreateBubble(bubble);

  // VirtualCardEnrollBubbleController::IsEnrollmentInProgress() == true when
  // the bubble has been accepted and the enrollment is still in progress. In
  // this case we do not want to offer enrollment again on reshow.
  if (controller->IsEnrollmentInProgress()) {
    bubble->SwitchToLoadingState();
  }

  bubble->ShowForReason(is_user_gesture
                            ? VirtualCardEnrollBubbleViews::USER_GESTURE
                            : VirtualCardEnrollBubbleViews::AUTOMATIC);
  if (!bubble_anchor_util::IsHighlightable(anchor)) {
    bubble->SetHighlightedElement(
        kAutofillVirtualCardEnrollPageActionElementId);
  }

  return bubble;
}

AutofillBubbleBase*
AutofillBubbleHandlerImpl::ShowVirtualCardEnrollConfirmationBubble(
    content::WebContents* web_contents,
    VirtualCardEnrollBubbleController* controller) {
  views::BubbleAnchor anchor =
      toolbar_button_provider_->GetBubbleAnchor(kActionVirtualCardEnroll);
  base::OnceCallback<void(PaymentsUiClosedReason)> callback =
      controller->GetOnBubbleClosedCallback();
  const SavePaymentMethodAndVirtualCardEnrollConfirmationUiParams& ui_params =
      controller->GetConfirmationUiParams();

  return ShowSaveCardAndVirtualCardEnrollConfirmationBubble(
      anchor, web_contents, std::move(callback),
      kAutofillVirtualCardEnrollPageActionElementId, ui_params);
}

AutofillBubbleBase* AutofillBubbleHandlerImpl::ShowMandatoryReauthBubble(
    content::WebContents* web_contents,
    MandatoryReauthBubbleController* controller,
    bool is_user_gesture,
    MandatoryReauthBubbleType bubble_type) {
  auto anchor =
      toolbar_button_provider_->GetBubbleAnchor(kActionAutofillMandatoryReauth);

  switch (bubble_type) {
    case MandatoryReauthBubbleType::kOptIn: {
      MandatoryReauthOptInBubbleView* bubble =
          new MandatoryReauthOptInBubbleView(anchor, web_contents, controller);
      bubble->SetHighlightedElement(
          kAutofillMandatoryReauthPageActionElementId);
      views::BubbleDialogDelegateView::CreateBubble(bubble);
      bubble->Show(is_user_gesture ? LocationBarBubbleDelegateView::USER_GESTURE
                                   : LocationBarBubbleDelegateView::AUTOMATIC);
      return bubble;
    }
    case MandatoryReauthBubbleType::kConfirmation: {
      MandatoryReauthConfirmationBubbleView* bubble =
          new MandatoryReauthConfirmationBubbleView(anchor, web_contents,
                                                    controller);
      bubble->SetHighlightedElement(
          kAutofillMandatoryReauthPageActionElementId);
      views::BubbleDialogDelegateView::CreateBubble(bubble);
      bubble->Show(is_user_gesture ? LocationBarBubbleDelegateView::USER_GESTURE
                                   : LocationBarBubbleDelegateView::AUTOMATIC);
      return bubble;
    }
    case MandatoryReauthBubbleType::kInactive:
      NOTREACHED();
  }
}

AutofillBubbleBase* AutofillBubbleHandlerImpl::ShowSaveCardConfirmationBubble(
    content::WebContents* web_contents,
    SaveCardBubbleController* controller) {
  views::BubbleAnchor anchor = toolbar_button_provider_->GetBubbleAnchor(
      kActionShowPaymentsBubbleOrPage);
  base::OnceCallback<void(PaymentsUiClosedReason)> callback =
      controller->GetOnBubbleClosedCallback();

  const SavePaymentMethodAndVirtualCardEnrollConfirmationUiParams& ui_params =
      controller->GetConfirmationUiParams();

  return ShowSaveCardAndVirtualCardEnrollConfirmationBubble(
      anchor, web_contents, std::move(callback),
      kAutofillSavePaymentsPageActionElementId, ui_params);
}

AutofillBubbleBase* AutofillBubbleHandlerImpl::ShowSaveIbanConfirmationBubble(
    content::WebContents* web_contents,
    IbanBubbleController* controller) {
  views::BubbleAnchor anchor = toolbar_button_provider_->GetBubbleAnchor(
      kActionShowPaymentsBubbleOrPage);
  base::OnceCallback<void(PaymentsUiClosedReason)> callback =
      controller->GetOnBubbleClosedCallback();

  return ShowSaveCardAndVirtualCardEnrollConfirmationBubble(
      anchor, web_contents, std::move(callback),
      kAutofillSavePaymentsPageActionElementId,
      controller->GetConfirmationUiParams());
}

AutofillBubbleBase* AutofillBubbleHandlerImpl::ShowOmniboxAutofillBubble(
    content::WebContents* web_contents,
    OmniboxAutofillBubbleController* controller) {
  return ShowBubble<OmniboxAutofillBubbleView>(
      toolbar_button_provider_, kActionAutofillPayment,
      kAutofillPaymentIconElementId, /*is_user_gesture=*/true, web_contents,
      controller);
}

AutofillBubbleBase* AutofillBubbleHandlerImpl::ShowPaymentsChurnedUsersBubble(
    content::WebContents* web_contents,
    PaymentsChurnedUsersBubbleController* controller,
    bool is_user_gesture) {
  return ShowBubble<PaymentsChurnedUsersBubbleView>(
      toolbar_button_provider_, kActionShowPaymentsChurnedUsersBubble,
      kPaymentsChurnedUsersBubbleId, is_user_gesture, web_contents, controller);
}

AutofillBubbleBase*
AutofillBubbleHandlerImpl::ShowPaymentsChurnedUsersConfirmationBubble(
    content::WebContents* web_contents,
    PaymentsChurnedUsersBubbleController* controller) {
  views::BubbleAnchor anchor = toolbar_button_provider_->GetBubbleAnchor(
      kActionShowPaymentsChurnedUsersBubble);
  base::OnceCallback<void(PaymentsUiClosedReason)> callback =
      controller->GetConfirmationBubbleClosedCallback();

  return ShowSaveCardAndVirtualCardEnrollConfirmationBubble(
      anchor, web_contents, std::move(callback), kPaymentsChurnedUsersBubbleId,
      controller->GetConfirmationUiParams());
}

AutofillBubbleBase* AutofillBubbleHandlerImpl::ShowWalletReminderNoticeBubble(
    content::WebContents* web_contents,
    WalletReminderNoticeBubbleController* controller,
    bool is_user_gesture) {
  return ShowBubble<WalletReminderNoticeBubbleView>(
      toolbar_button_provider_, kActionWalletReminderNotice,
      kPageActionWalletReminderNoticeElementId, is_user_gesture, web_contents,
      controller);
}

AutofillBubbleBase*
AutofillBubbleHandlerImpl::ShowSaveCardAndVirtualCardEnrollConfirmationBubble(
    views::BubbleAnchor anchor,
    content::WebContents* web_contents,
    base::OnceCallback<void(PaymentsUiClosedReason)> controller_hide_callback,
    ui::ElementIdentifier highlight_element,
    SavePaymentMethodAndVirtualCardEnrollConfirmationUiParams ui_params) {
  SavePaymentMethodAndVirtualCardEnrollConfirmationBubbleViews* bubble =
      new SavePaymentMethodAndVirtualCardEnrollConfirmationBubbleViews(
          anchor, web_contents, std::move(controller_hide_callback),
          std::move(ui_params));

  if (!bubble_anchor_util::IsHighlightable(anchor)) {
    bubble->SetHighlightedElement(highlight_element);
  }
  views::BubbleDialogDelegateView::CreateBubble(bubble);
  bubble->ShowForReason(LocationBarBubbleDelegateView::AUTOMATIC);

  return bubble;
}

}  // namespace autofill
