// Copyright 2023 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/autofill/payments/iban_bubble_controller_impl.h"

#include <string>
#include <string_view>

#include "chrome/browser/autofill/personal_data_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/ui/actions/chrome_action_id.h"
#include "chrome/browser/ui/autofill/autofill_bubble_base.h"
#include "chrome/browser/ui/autofill/autofill_bubble_handler.h"
#include "chrome/browser/ui/autofill/payments/save_iban_ui.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/page_action/page_action_icon_type.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "components/autofill/core/browser/data_manager/personal_data_manager.h"
#include "components/autofill/core/browser/data_model/payments/iban.h"
#include "components/autofill/core/browser/metrics/autofill_metrics.h"
#include "components/autofill/core/browser/metrics/payments/iban_metrics.h"
#include "components/autofill/core/browser/ui/payments/payments_ui_closed_reasons.h"
#include "components/autofill/core/common/autofill_payments_features.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/strings/grit/components_strings.h"
#include "components/tabs/public/tab_interface.h"
#include "ui/base/l10n/l10n_util.h"

namespace autofill {

// static
IbanBubbleController* IbanBubbleController::GetOrCreate(
    content::WebContents* web_contents) {
  if (!web_contents) {
    return nullptr;
  }

  IbanBubbleControllerImpl::CreateForWebContents(web_contents);
  return IbanBubbleControllerImpl::FromWebContents(web_contents);
}

IbanBubbleControllerImpl::~IbanBubbleControllerImpl() = default;

void IbanBubbleControllerImpl::OfferLocalSave(
    const Iban& iban,
    bool should_show_prompt,
    payments::PaymentsAutofillClient::SaveIbanPromptCallback
        save_iban_prompt_callback) {
  // Don't show the bubble if it's already visible.
  if (bubble_view() || !MaySetUpBubble()) {
    return;
  }

  SetupLocalSave(iban, std::move(save_iban_prompt_callback));
  if (should_show_prompt) {
    QueueOrShowBubble();
  } else {
    ShowIconOnly();
  }
}

void IbanBubbleControllerImpl::OfferUploadSave(
    const Iban& iban,
    LegalMessageLines legal_message_lines,
    bool should_show_prompt,
    payments::PaymentsAutofillClient::SaveIbanPromptCallback
        save_iban_prompt_callback) {
  // Don't show the bubble if it's already visible.
  if (bubble_view() || !MaySetUpBubble()) {
    return;
  }

  SetupUploadSave(iban, std::move(legal_message_lines),
                  std::move(save_iban_prompt_callback));
  if (should_show_prompt) {
    QueueOrShowBubble();
  } else {
    ShowIconOnly();
  }
}

void IbanBubbleControllerImpl::SetupLocalSave(
    Iban iban,
    payments::PaymentsAutofillClient::SaveIbanPromptCallback
        save_iban_prompt_callback) {
  was_bubble_shown_ = false;
  iban_ = std::move(iban);
  is_reshow_ = false;
  is_upload_save_ = false;
  legal_message_lines_.clear();
  save_iban_prompt_callback_ = std::move(save_iban_prompt_callback);
  current_bubble_type_ = IbanBubbleType::kLocalSave;
  // Save callback should not be null for IBAN save.
  CHECK(!save_iban_prompt_callback_.is_null());
}

void IbanBubbleControllerImpl::SetupUploadSave(
    Iban iban,
    LegalMessageLines legal_message_lines,
    payments::PaymentsAutofillClient::SaveIbanPromptCallback
        save_iban_prompt_callback) {
  was_bubble_shown_ = false;
  iban_ = std::move(iban);
  is_reshow_ = false;
  is_upload_save_ = true;
  legal_message_lines_ = std::move(legal_message_lines);
  save_iban_prompt_callback_ = std::move(save_iban_prompt_callback);
  current_bubble_type_ = IbanBubbleType::kUploadSave;
  // Save callback should not be null for IBAN save.
  CHECK(!save_iban_prompt_callback_.is_null());
}

void IbanBubbleControllerImpl::ReshowBubble() {
  // Don't show the bubble if it's already visible.
  if (bubble_view()) {
    return;
  }

  is_reshow_ = true;
  CHECK(current_bubble_type_ != IbanBubbleType::kInactive);
  if (current_bubble_type_ == IbanBubbleType::kLocalSave ||
      current_bubble_type_ == IbanBubbleType::kUploadSave) {
    CHECK(!save_iban_prompt_callback_.is_null());
  } else {
    CHECK(current_bubble_type_ == IbanBubbleType::kManageSavedIban ||
          current_bubble_type_ == IbanBubbleType::kUploadInProgress);
  }
  QueueOrShowBubble(/*force_show=*/true);
}

void IbanBubbleControllerImpl::ShowConfirmationBubbleView(
    bool iban_saved,
    bool hit_max_strikes) {
  DoNotShowNextQueuedBubbleGuard guard = DoNotShowNextQueuedBubble();

  // Hide the current bubble if still showing.
  HideBubble(/*initiated_by_bubble_manager=*/false);

  is_reshow_ = false;
  current_bubble_type_ = IbanBubbleType::kUploadCompleted;
  confirmation_ui_params_ =
      iban_saved ? SavePaymentMethodAndVirtualCardEnrollConfirmationUiParams::
                       CreateForSaveIbanSuccess()
                 : SavePaymentMethodAndVirtualCardEnrollConfirmationUiParams::
                       CreateForSaveIbanFailure(hit_max_strikes);

  // Show upload confirmation bubble.
  AutofillBubbleHandler* autofill_bubble_handler = GetAutofillBubbleHandler();
  SetBubbleView(*autofill_bubble_handler->ShowSaveIbanConfirmationBubble(
      web_contents(), this));
  UpdatePageActionIcon();

  // Auto close confirmation bubble when IBAN saved is successful.
  if (iban_saved) {
    auto_close_confirmation_timer_.Start(
        FROM_HERE, kAutoCloseConfirmationBubbleWaitSec,
        base::BindOnce(&IbanBubbleControllerImpl::HideBubble,
                       base::Unretained(this),
                       /*initiated_by_bubble_manager=*/false));
  }
}

std::u16string IbanBubbleControllerImpl::GetWindowTitle() const {
  switch (current_bubble_type_) {
    case IbanBubbleType::kLocalSave:
      return l10n_util::GetStringUTF16(
          IDS_AUTOFILL_SAVE_IBAN_PROMPT_TITLE_LOCAL);
    case IbanBubbleType::kUploadSave:
    case IbanBubbleType::kUploadInProgress:
      return l10n_util::GetStringUTF16(
          base::FeatureList::IsEnabled(
              features::kAutofillEnableWalletBrandingV2)
              ? IDS_AUTOFILL_SAVE_IBAN_TO_WALLET_PROMPT_TITLE
              : IDS_AUTOFILL_SAVE_IBAN_PROMPT_TITLE_SERVER);
    case IbanBubbleType::kManageSavedIban:
      return l10n_util::GetStringUTF16(IDS_AUTOFILL_IBAN_SAVED);
    case IbanBubbleType::kUploadCompleted:
    case IbanBubbleType::kInactive:
      NOTREACHED();
  }
}

std::u16string IbanBubbleControllerImpl::GetExplanatoryMessage() const {
  if (current_bubble_type_ == IbanBubbleType::kUploadSave) {
    return l10n_util::GetStringUTF16(
        base::FeatureList::IsEnabled(features::kAutofillEnableWalletBranding)
            ? IDS_AUTOFILL_UPLOAD_IBAN_TO_WALLET_PROMPT_EXPLANATION
            : IDS_AUTOFILL_UPLOAD_IBAN_PROMPT_EXPLANATION);
  }
  return std::u16string();
}

std::u16string IbanBubbleControllerImpl::GetAcceptButtonText() const {
  switch (current_bubble_type_) {
    case IbanBubbleType::kLocalSave:
    case IbanBubbleType::kUploadSave:
      return l10n_util::GetStringUTF16(
          IDS_AUTOFILL_SAVE_IBAN_BUBBLE_SAVE_ACCEPT);
    case IbanBubbleType::kManageSavedIban:
      return l10n_util::GetStringUTF16(IDS_AUTOFILL_DONE);
    case IbanBubbleType::kUploadInProgress:
      return std::u16string();
    case IbanBubbleType::kUploadCompleted:
    case IbanBubbleType::kInactive:
      NOTREACHED();
  }
}

std::u16string IbanBubbleControllerImpl::GetDeclineButtonText() const {
  switch (current_bubble_type_) {
    case IbanBubbleType::kLocalSave:
    case IbanBubbleType::kUploadSave:
      return l10n_util::GetStringUTF16(
          IDS_AUTOFILL_SAVE_IBAN_BUBBLE_SAVE_NO_THANKS);
    case IbanBubbleType::kUploadInProgress:
      return std::u16string();
    case IbanBubbleType::kManageSavedIban:
    case IbanBubbleType::kUploadCompleted:
    case IbanBubbleType::kInactive:
      NOTREACHED();
  }
}

AccountInfo IbanBubbleControllerImpl::GetAccountInfo() {
  // The results of this call should not be cached because the user can update
  // their account info at any time.
  Profile* profile = GetProfile();
  if (!profile) {
    return AccountInfo();
  }
  signin::IdentityManager* identity_manager =
      IdentityManagerFactory::GetForProfile(profile);
  if (!identity_manager) {
    return AccountInfo();
  }
  PersonalDataManager* personal_data_manager =
      PersonalDataManagerFactory::GetForBrowserContext(profile);
  if (!personal_data_manager) {
    return AccountInfo();
  }

  return identity_manager->FindExtendedAccountInfo(
      personal_data_manager->payments_data_manager()
          .GetAccountInfoForPaymentsServer());
}

const Iban& IbanBubbleControllerImpl::GetIban() const {
  return iban_;
}

base::OnceCallback<void(PaymentsUiClosedReason)>
IbanBubbleControllerImpl::GetOnBubbleClosedCallback() {
  return base::BindOnce(&IbanBubbleControllerImpl::OnBubbleClosed,
                        weak_ptr_factory_.GetWeakPtr());
}

void IbanBubbleControllerImpl::OnAcceptButton(std::u16string_view nickname) {
  switch (current_bubble_type_) {
    case IbanBubbleType::kLocalSave:
      CHECK(!save_iban_prompt_callback_.is_null());
      // Show an animated IBAN saved confirmation message next time
      // UpdatePageActionIcon() is called.
      should_show_iban_saved_label_animation_ = true;
      autofill_metrics::LogSaveIbanPromptResultSavedWithNicknameMetric(
          !nickname.empty(), /*is_upload_save=*/false);
      iban_.set_nickname(std::u16string(nickname));
      std::move(save_iban_prompt_callback_)
          .Run(payments::PaymentsAutofillClient::SaveIbanOfferUserDecision::
                   kAccepted,
               nickname);
      return;
    case IbanBubbleType::kUploadSave:
      CHECK(!save_iban_prompt_callback_.is_null());
      autofill_metrics::LogSaveIbanPromptResultSavedWithNicknameMetric(
          !nickname.empty(), /*is_upload_save=*/true);
      // Log metrics now for the upload save IBAN. The upload case is special
      // because we don't immediately close the bubble (at which time the other
      // metrics are logged) after OnAcceptButton() and logging now aligns the
      // timing of the log with the other cases.
      autofill_metrics::LogSaveIbanPromptResultMetric(
          autofill_metrics::SaveIbanPromptResult::kAccepted, is_reshow_,
          /*is_upload_save=*/true);
      iban_.set_nickname(std::u16string(nickname));
      current_bubble_type_ = IbanBubbleType::kUploadInProgress;
      std::move(save_iban_prompt_callback_)
          .Run(payments::PaymentsAutofillClient::SaveIbanOfferUserDecision::
                   kAccepted,
               nickname);
      return;
    case IbanBubbleType::kManageSavedIban:
      return;
    case IbanBubbleType::kUploadInProgress:
    case IbanBubbleType::kUploadCompleted:
    case IbanBubbleType::kInactive:
      NOTREACHED();
  }
}

void IbanBubbleControllerImpl::OnLegalMessageLinkClicked(const GURL& url) {
  web_contents()->OpenURL(
      content::OpenURLParams(url, content::Referrer(),
                             WindowOpenDisposition::NEW_FOREGROUND_TAB,
                             ui::PAGE_TRANSITION_LINK, false),
      /*navigation_handle_callback=*/{});
}

void IbanBubbleControllerImpl::OnManageSavedIbanExtraButtonClicked() {
  CHECK(current_bubble_type_ == IbanBubbleType::kManageSavedIban);
  tabs::TabInterface* tab = tabs::TabInterface::GetFromContents(web_contents());
  CHECK(tab);
  CHECK(tab->GetBrowserWindowInterface());
  chrome::ShowSettingsSubPage(tab->GetBrowserWindowInterface(),
                              chrome::kPaymentsSubPage);
  OnBubbleClosed(PaymentsUiClosedReason::kClosed);
}

void IbanBubbleControllerImpl::OnBubbleDiscarded() {
  LogBubbleCloseMetrics(was_bubble_shown_
                            ? PaymentsUiClosedReason::kNotInteracted
                            : PaymentsUiClosedReason::kUnknown);
}

void IbanBubbleControllerImpl::LogBubbleCloseMetrics(
    PaymentsUiClosedReason closed_reason) {
  auto get_metric = [](PaymentsUiClosedReason reason) {
    switch (reason) {
      case PaymentsUiClosedReason::kAccepted:
        return autofill_metrics::SaveIbanPromptResult::kAccepted;
      case PaymentsUiClosedReason::kCancelled:
        return autofill_metrics::SaveIbanPromptResult::kCancelled;
      case PaymentsUiClosedReason::kClosed:
        return autofill_metrics::SaveIbanPromptResult::kClosed;
      case PaymentsUiClosedReason::kNotInteracted:
        return autofill_metrics::SaveIbanPromptResult::kNotInteracted;
      case PaymentsUiClosedReason::kLostFocus:
        return autofill_metrics::SaveIbanPromptResult::kLostFocus;
      case PaymentsUiClosedReason::kUnknown:
        return autofill_metrics::SaveIbanPromptResult::kUnknown;
    }
  };

  // Log save IBAN prompt result according to the closed reason.
  if (current_bubble_type_ == IbanBubbleType::kLocalSave ||
      current_bubble_type_ == IbanBubbleType::kUploadSave ||
      current_bubble_type_ == IbanBubbleType::kUploadInProgress) {
    switch (current_bubble_type_) {
      case IbanBubbleType::kLocalSave:
      case IbanBubbleType::kUploadSave:
        autofill_metrics::LogSaveIbanPromptResultMetric(
            get_metric(closed_reason), is_reshow_,
            /*is_upload_save=*/
            (current_bubble_type_ == IbanBubbleType::kUploadSave));
        break;
      case IbanBubbleType::kInactive:
      case IbanBubbleType::kManageSavedIban:
      case IbanBubbleType::kUploadCompleted:
      case IbanBubbleType::kUploadInProgress:
        break;
    }
  }
}

void IbanBubbleControllerImpl::OnBubbleClosed(
    PaymentsUiClosedReason closed_reason) {
  if (current_bubble_type_ == IbanBubbleType::kLocalSave ||
      current_bubble_type_ == IbanBubbleType::kUploadSave) {
    if (closed_reason == PaymentsUiClosedReason::kCancelled) {
      std::move(save_iban_prompt_callback_)
          .Run(payments::PaymentsAutofillClient::SaveIbanOfferUserDecision::
                   kDeclined,
               /*nickname=*/u"");
    } else if (closed_reason == PaymentsUiClosedReason::kClosed) {
      std::move(save_iban_prompt_callback_)
          .Run(payments::PaymentsAutofillClient::SaveIbanOfferUserDecision::
                   kIgnored,
               /*nickname=*/u"");
    }
  }

  ResetBubbleViewAndInformBubbleManager();

  if (!bubble_hide_initiated_by_bubble_manager_) {
    LogBubbleCloseMetrics(closed_reason);
  }

  if (current_bubble_type_ == IbanBubbleType::kUploadCompleted) {
    current_bubble_type_ = IbanBubbleType::kInactive;

    UpdatePageActionIcon();
    confirmation_ui_params_.reset();
    return;
  }

  // Handles `current_bubble_type_` change according to its current type and the
  // `closed_reason`.
  if (closed_reason == PaymentsUiClosedReason::kAccepted) {
    if (current_bubble_type_ == IbanBubbleType::kLocalSave ||
        current_bubble_type_ == IbanBubbleType::kUploadSave) {
      current_bubble_type_ = IbanBubbleType::kManageSavedIban;
    } else {
      current_bubble_type_ = IbanBubbleType::kInactive;
    }
  } else if (closed_reason == PaymentsUiClosedReason::kCancelled ||
             closed_reason == PaymentsUiClosedReason::kClosed) {
    if (current_bubble_type_ != IbanBubbleType::kManageSavedIban) {
      current_bubble_type_ = IbanBubbleType::kInactive;
    }
  }
  UpdatePageActionIcon();
  confirmation_ui_params_.reset();
}

IbanBubbleControllerImpl::IbanBubbleControllerImpl(
    content::WebContents* web_contents)
    : AutofillBubbleControllerBase(web_contents),
      content::WebContentsUserData<IbanBubbleControllerImpl>(*web_contents) {}

IbanBubbleType IbanBubbleControllerImpl::GetIbanBubbleType() const {
  return current_bubble_type_;
}

std::u16string IbanBubbleControllerImpl::GetSavePaymentIconTooltipText() const {
  switch (current_bubble_type_) {
    case IbanBubbleType::kLocalSave:
    case IbanBubbleType::kUploadSave:
    case IbanBubbleType::kManageSavedIban:
      return l10n_util::GetStringUTF16(IDS_TOOLTIP_SAVE_IBAN);
    case IbanBubbleType::kUploadInProgress:
    case IbanBubbleType::kUploadCompleted:
    case IbanBubbleType::kInactive:
      return std::u16string();
  }
}

bool IbanBubbleControllerImpl::ShouldShowSavingPaymentAnimation() const {
  return false;
}

bool IbanBubbleControllerImpl::ShouldShowPaymentSavedLabelAnimation() const {
  return should_show_iban_saved_label_animation_;
}

void IbanBubbleControllerImpl::OnAnimationEnded() {
  // Do not repeat the animation next time UpdatePageActionIcon() is called,
  // unless explicitly set somewhere else.
  should_show_iban_saved_label_animation_ = false;
}

bool IbanBubbleControllerImpl::IsIconVisible() const {
  // If there is no bubble to show, then there should be no icon.
  return current_bubble_type_ != IbanBubbleType::kInactive;
}

AutofillBubbleBase* IbanBubbleControllerImpl::GetPaymentBubbleView() const {
  return bubble_view();
}

int IbanBubbleControllerImpl::GetSaveSuccessAnimationStringId() const {
  return IDS_AUTOFILL_IBAN_SAVED;
}

const SavePaymentMethodAndVirtualCardEnrollConfirmationUiParams&
IbanBubbleControllerImpl::GetConfirmationUiParams() const {
  CHECK(confirmation_ui_params_.has_value());
  return confirmation_ui_params_.value();
}

std::optional<PageActionIconType>
IbanBubbleControllerImpl::GetPageActionIconType() {
  return PageActionIconType::kSaveIban;
}

#if !BUILDFLAG(IS_ANDROID)
std::optional<actions::ActionId>
IbanBubbleControllerImpl::GetActionIdForPageAction() {
  return kActionShowPaymentsBubbleOrPage;
}

std::optional<std::u16string>
IbanBubbleControllerImpl::GetPageActionTooltipText() {
  return GetSavePaymentIconTooltipText();
}
#endif  // !BUILDFLAG(IS_ANDROID)

void IbanBubbleControllerImpl::DoShowBubble() {
  AutofillBubbleHandler* autofill_bubble_handler = GetAutofillBubbleHandler();
  SetBubbleView(*autofill_bubble_handler->ShowIbanBubble(
      web_contents(), this,
      /*is_user_gesture=*/is_reshow_, current_bubble_type_));
  CHECK(bubble_view());
  CHECK(current_bubble_type_ != IbanBubbleType::kInactive);

  switch (current_bubble_type_) {
    case IbanBubbleType::kLocalSave:
      autofill_metrics::LogSaveIbanPromptOfferMetric(
          autofill_metrics::SaveIbanPromptOffer::kShown, is_reshow_,
          /*is_upload_save=*/false);
      break;
    case IbanBubbleType::kUploadSave:
      autofill_metrics::LogSaveIbanPromptOfferMetric(
          autofill_metrics::SaveIbanPromptOffer::kShown, is_reshow_,
          /*is_upload_save=*/true);
      break;
    case IbanBubbleType::kManageSavedIban:
      // TODO(crbug.com/40233611): Add metrics for manage saved IBAN mode.
      break;
    case IbanBubbleType::kUploadInProgress:
      break;
    case IbanBubbleType::kUploadCompleted:
    case IbanBubbleType::kInactive:
      NOTREACHED();
  }

  if (observer_for_testing_) {
    observer_for_testing_->OnBubbleShown();
  }
}

bool IbanBubbleControllerImpl::CanBeReshown() const {
  return current_bubble_type_ != IbanBubbleType::kUploadCompleted &&
         current_bubble_type_ != IbanBubbleType::kInactive;
}

BubbleType IbanBubbleControllerImpl::GetBubbleType() const {
  return BubbleType::kSaveIban;
}

base::WeakPtr<BubbleControllerBase>
IbanBubbleControllerImpl::GetBubbleControllerBaseWeakPtr() {
  return weak_ptr_factory_.GetWeakPtr();
}

Profile* IbanBubbleControllerImpl::GetProfile() {
  if (!web_contents()) {
    return nullptr;
  }
  return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
}

AutofillBubbleHandler* IbanBubbleControllerImpl::GetAutofillBubbleHandler() {
  tabs::TabInterface* tab = tabs::TabInterface::GetFromContents(web_contents());
  CHECK(tab);
  BrowserWindowInterface* browser = tab->GetBrowserWindowInterface();
  CHECK(browser);
  return AutofillBubbleHandler::Get(browser->GetUnownedUserDataHost());
}

void IbanBubbleControllerImpl::ShowIconOnly() {
  CHECK(current_bubble_type_ != IbanBubbleType::kInactive);
  if (current_bubble_type_ == IbanBubbleType::kLocalSave ||
      current_bubble_type_ == IbanBubbleType::kUploadSave) {
    CHECK(!save_iban_prompt_callback_.is_null());
  } else {
    CHECK(current_bubble_type_ == IbanBubbleType::kManageSavedIban);
  }
  CHECK(!bubble_view());

  // Show the icon only. The bubble can still be displayed if the user
  // explicitly clicks the icon.
  UpdatePageActionIcon();

  switch (current_bubble_type_) {
    case IbanBubbleType::kLocalSave:
      autofill_metrics::LogSaveIbanPromptOfferMetric(
          autofill_metrics::SaveIbanPromptOffer::kNotShownMaxStrikesReached,
          is_reshow_, /*is_upload_save=*/false);
      break;
    case IbanBubbleType::kUploadSave:
      autofill_metrics::LogSaveIbanPromptOfferMetric(
          autofill_metrics::SaveIbanPromptOffer::kNotShownMaxStrikesReached,
          is_reshow_, /*is_upload_save=*/true);
      break;
    case IbanBubbleType::kManageSavedIban:
    case IbanBubbleType::kUploadCompleted:
    case IbanBubbleType::kUploadInProgress:
      break;
    case IbanBubbleType::kInactive:
      NOTREACHED();
  }

  if (observer_for_testing_) {
    observer_for_testing_->OnIconShown();
  }
}

bool IbanBubbleControllerImpl::IsUploadSave() const {
  return is_upload_save_;
}

const LegalMessageLines& IbanBubbleControllerImpl::GetLegalMessageLines()
    const {
  return legal_message_lines_;
}

WEB_CONTENTS_USER_DATA_KEY_IMPL(IbanBubbleControllerImpl);

}  // namespace autofill
