// Copyright 2024 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/commerce/discounts_bubble_dialog_view.h"

#include <memory>
#include <string>
#include <utility>

#include "base/i18n/time_formatting.h"
#include "chrome/browser/ui/commerce/commerce_ui_tab_helper.h"
#include "chrome/browser/ui/tabs/public/tab_features.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/commerce/discounts_coupon_code_label_view.h"
#include "chrome/browser/ui/views/controls/page_switcher_view.h"
#include "chrome/browser/ui/views/controls/subpage_view.h"
#include "chrome/grit/browser_resources.h"
#include "components/commerce/core/commerce_types.h"
#include "components/commerce/core/metrics/discounts_metric_collector.h"
#include "components/strings/grit/components_strings.h"
#include "components/tabs/public/tab_interface.h"
#include "components/url_formatter/elide_url.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/styled_label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/metadata/view_factory.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/view_tracker.h"
#include "url/gurl.h"

DEFINE_ELEMENT_IDENTIFIER_VALUE(kDiscountsBubbleDialogId);
DEFINE_ELEMENT_IDENTIFIER_VALUE(kDiscountsBubbleMainPageId);
DEFINE_ELEMENT_IDENTIFIER_VALUE(kDiscountsBubbleTermsAndConditionLabelId);
DEFINE_ELEMENT_IDENTIFIER_VALUE(kDiscountsBubbleTermsAndConditionPageId);

// DiscountsBubbleDialogView
DiscountsBubbleDialogView::DiscountsBubbleDialogView(
    views::BubbleAnchor anchor,
    content::WebContents* web_contents,
    const commerce::DiscountInfo& discount_info)
    : LocationBarBubbleDelegateView(anchor, web_contents, true),
      discount_info_(discount_info),
      ukm_source_id_(
          web_contents->GetPrimaryMainFrame()->GetPageUkmSourceId()) {
  SetProperty(views::kElementIdentifierKey, kDiscountsBubbleDialogId);

  SetShowCloseButton(true);
  SetCloseCallback(base::BindOnce(&DiscountsBubbleDialogView::OnDialogClosing,
                                  weak_factory_.GetWeakPtr()));

  set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
      views::DISTANCE_BUBBLE_PREFERRED_WIDTH));
  SetLayoutManager(std::make_unique<views::FillLayout>());
  auto dialog_insets =
      ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
          views::DialogContentType::kControl, views::DialogContentType::kText);
  set_margins(
      gfx::Insets::TLBR(dialog_insets.top(), 0, dialog_insets.bottom(), 0));
  SetButtons(static_cast<int>(ui::mojom::DialogButton::kNone));

  GURL url = web_contents->GetLastCommittedURL();
  std::string seller_domain = url.spec();

  page_container_ = AddChildView(std::make_unique<PageSwitcherView>(
      CreateMainPageContent(discount_info, seller_domain)));
}

DiscountsBubbleDialogView::~DiscountsBubbleDialogView() = default;

void DiscountsBubbleDialogView::AddedToWidget() {
  GetBubbleFrameView()->SetHeaderView(CreateMainPageHeaderView());
  GetBubbleFrameView()->SetTitleView(CreateMainPageTitleView(discount_info_));
}

std::unique_ptr<views::View>
DiscountsBubbleDialogView::CreateMainPageHeaderView() {
  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();

  auto image_view =
      std::make_unique<views::ImageView>(bundle.GetThemedLottieImageNamed(
          IDR_SHOPPING_DISCOUNTS_AVAILABLE_LOTTIE));
  image_view->GetViewAccessibility().SetIsInvisible(true);

  return image_view;
}

std::unique_ptr<views::View> DiscountsBubbleDialogView::CreateMainPageTitleView(
    const commerce::DiscountInfo& discount_info) {
  auto title_view = std::make_unique<views::Label>(
      base::ASCIIToUTF16(discount_info_.description_detail),
      views::style::CONTEXT_DIALOG_TITLE);
  title_view->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  title_view->SetMultiLine(true);
  return title_view;
}

std::unique_ptr<views::View> DiscountsBubbleDialogView::CreateMainPageContent(
    const commerce::DiscountInfo& discount_info,
    const std::string& seller_domain) {
  auto main_page_view = std::make_unique<views::View>();

  main_page_view->SetProperty(views::kElementIdentifierKey,
                              kDiscountsBubbleMainPageId);
  gfx::Insets dialog_insets =
      ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
          views::DialogContentType::kControl, views::DialogContentType::kText);

  auto* layout =
      main_page_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
          views::BoxLayout::Orientation::kVertical,
          gfx::Insets::TLBR(0, dialog_insets.left(), 0, dialog_insets.right()),
          ChromeLayoutProvider::Get()->GetDistanceMetric(
              views::DISTANCE_UNRELATED_CONTROL_VERTICAL)));
  layout->set_cross_axis_alignment(
      views::BoxLayout::CrossAxisAlignment::kStart);

  // coupon code
  main_page_view->AddChildView(std::make_unique<DiscountsCouponCodeLabelView>(
      base::ASCIIToUTF16(discount_info.discount_code.value()),
      base::BindRepeating(&DiscountsBubbleDialogView::CopyButtonClicked,
                          weak_factory_.GetWeakPtr())));

  // additional info with expiration date, and terms and conditions
  auto* additional_info_label = main_page_view->AddChildView(
      views::Builder<views::StyledLabel>()
          .SetDefaultTextStyle(views::style::STYLE_SECONDARY)
          .SetTextContext(views::style::CONTEXT_DIALOG_BODY_TEXT)
          .SetHorizontalAlignment(gfx::ALIGN_LEFT)
          .Build());

  auto additional_info_text =
      discount_info.expiry_time_sec.has_value()
          ? l10n_util::GetStringFUTF16(
                IDS_DISCOUNT_USE_THIS_CODE_AT_CHECKOUT_WITH_EXPIRATION_DATE,
                TimeFormatShortDate(base::Time::FromSecondsSinceUnixEpoch(
                    discount_info.expiry_time_sec.value())))
          : l10n_util::GetStringUTF16(IDS_DISCOUNT_USE_THIS_CODE_AT_CHECKOUT);

  if (discount_info.terms_and_conditions.has_value() &&
      !discount_info.terms_and_conditions.value().empty()) {
    std::vector<size_t> offsets;
    additional_info_text = l10n_util::GetStringFUTF16(
        IDS_TWO_STRINGS_CONNECTOR_WITH_SPACE, additional_info_text,
        l10n_util::GetStringUTF16(IDS_SEE_SELLER_TERMS_AND_CONDITIONS),
        &offsets);
    additional_info_label->SetText(additional_info_text);
    size_t terms_and_conditions_offset = offsets[1];
    base::RepeatingCallback<void()> callback = base::BindRepeating(
        &DiscountsBubbleDialogView::OpenTermsAndConditionsPage,
        weak_factory_.GetWeakPtr(), discount_info, seller_domain);
    views::StyledLabel::RangeStyleInfo terms_and_conditions_style_info =
        views::StyledLabel::RangeStyleInfo::CreateForLink(std::move(callback));
    additional_info_label->AddStyleRange(
        gfx::Range(terms_and_conditions_offset, additional_info_text.length()),
        terms_and_conditions_style_info);
    additional_info_label->SetProperty(
        views::kElementIdentifierKey, kDiscountsBubbleTermsAndConditionLabelId);
  } else {
    additional_info_label->SetText(additional_info_text);
  }

  return main_page_view;
}

void DiscountsBubbleDialogView::OpenMainPage(
    commerce::DiscountInfo discount_info,
    std::string seller_domain) {
  GetBubbleFrameView()->SetHeaderView(CreateMainPageHeaderView());
  GetBubbleFrameView()->SetTitleView(CreateMainPageTitleView(discount_info));
  page_container_->SwitchToPage(
      CreateMainPageContent(discount_info, seller_domain));
  GetBubbleFrameView()->SetFootnoteView(nullptr);
}

void DiscountsBubbleDialogView::OpenTermsAndConditionsPage(
    commerce::DiscountInfo discount_info,
    std::string seller_domain) {
  auto bubble_width = views::LayoutProvider::Get()->GetDistanceMetric(
      views::DISTANCE_BUBBLE_PREFERRED_WIDTH);

  gfx::FontList font_list = views::TypographyProvider::Get().GetFont(
      views::style::CONTEXT_LABEL, views::style::STYLE_PRIMARY);

  auto footer_message = l10n_util::GetStringFUTF16(
      IDS_SELLER_TERMS_AND_CONDITIONS_DIALOG_FOOTER,
      url_formatter::ElideHost(GURL(seller_domain), font_list, bubble_width));

  page_container_->SwitchToPage(
      views::Builder<SubpageView>(
          std::make_unique<SubpageView>(
              base::BindRepeating(&DiscountsBubbleDialogView::OpenMainPage,
                                  weak_factory_.GetWeakPtr(), discount_info,
                                  seller_domain),
              GetBubbleFrameView()))
          .SetProperty(views::kElementIdentifierKey,
                       kDiscountsBubbleTermsAndConditionPageId)
          .SetTitle(l10n_util::GetStringUTF16(
              IDS_SELLER_TERMS_AND_CONDITIONS_DIALOG_TITLE))
          .SetContentView(
              views::Builder<views::Label>()
                  .SetText(base::ASCIIToUTF16(
                      discount_info.terms_and_conditions.value()))
                  .SetMultiLine(true)
                  .SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT)
                  .Build())
          .SetHeaderView(nullptr)
          .SetFootnoteView(
              views::Builder<views::Label>()
                  .SetText(footer_message)
                  .SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT)
                  .SetMultiLine(true)
                  .SetAllowCharacterBreak(true)
                  .Build())
          .Build());
}

void DiscountsBubbleDialogView::CopyButtonClicked() {
  commerce::metrics::DiscountsMetricCollector::
      RecordDiscountsBubbleCopyButtonClicked(ukm_source_id_);

  auto* tab = tabs::TabInterface::MaybeGetFromContents(web_contents());
  if (!tab || !tab->GetTabFeatures()) {
    return;
  }

  auto* tab_helper = tab->GetTabFeatures()->commerce_ui_tab_helper();
  if (!tab_helper) {
    return;
  }

  tab_helper->OnDiscountsCouponCodeCopied();
}

void DiscountsBubbleDialogView::OnDialogClosing() {
  auto* tab = tabs::TabInterface::MaybeGetFromContents(web_contents());
  if (!tab || !tab->GetTabFeatures()) {
    return;
  }

  auto* tab_helper = tab->GetTabFeatures()->commerce_ui_tab_helper();
  if (!tab_helper) {
    return;
  }

  commerce::metrics::DiscountsMetricCollector::
      DiscountsBubbleCopyStatusOnBubbleClosed(
          tab_helper->IsDiscountsCouponCodeCopied(),
          tab_helper->GetDiscounts());
}

BEGIN_METADATA(DiscountsBubbleDialogView)
END_METADATA

// DiscountsBubbleCoordinator
DiscountsBubbleCoordinator::DiscountsBubbleCoordinator() = default;

DiscountsBubbleCoordinator::~DiscountsBubbleCoordinator() = default;

// WidgetObserver:
void DiscountsBubbleCoordinator::OnWidgetDestroying(views::Widget* widget) {
  CHECK(bubble_widget_observation_.IsObservingSource(widget));
  bubble_widget_observation_.Reset();

  std::move(on_dialog_closing_callback_).Run();
}

void DiscountsBubbleCoordinator::Show(
    views::BubbleAnchor anchor,
    content::WebContents* web_contents,
    const commerce::DiscountInfo& discount_info,
    base::OnceClosure on_dialog_closing_callback) {
  CHECK(!IsShowing());

  on_dialog_closing_callback_ = std::move(on_dialog_closing_callback);

  auto bubble = std::make_unique<DiscountsBubbleDialogView>(
      anchor, web_contents, discount_info);
  tracker_.SetView(bubble.get());
  auto* widget = DiscountsBubbleDialogView::CreateBubble(std::move(bubble));
  bubble_widget_observation_.Observe(widget);
  widget->Show();
}

void DiscountsBubbleCoordinator::Hide() {
  if (IsShowing()) {
    tracker_.view()->GetWidget()->Close();
  }
  tracker_.SetView(nullptr);
}

DiscountsBubbleDialogView* DiscountsBubbleCoordinator::GetBubble() const {
  return tracker_.view() ? views::AsViewClass<DiscountsBubbleDialogView>(
                               const_cast<views::View*>(tracker_.view()))
                         : nullptr;
}

bool DiscountsBubbleCoordinator::IsShowing() {
  return tracker_.view() != nullptr;
}
