// Copyright 2022 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/price_tracking_view.h"

#include "base/metrics/user_metrics.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
#include "chrome/browser/commerce/shopping_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/color/chrome_color_id.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/commerce/core/price_tracking_utils.h"
#include "components/omnibox/browser/vector_icons.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/ui_base_features.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/toggle_button.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout_view.h"
#include "ui/views/layout/flex_layout_view.h"
#include "ui/views/layout/layout_provider.h"

namespace {
constexpr int kIconSize = 20;
constexpr int kIconMargin = 8;

int GetIconMargin() {
  return kIconMargin;
}

gfx::Size GetIconSize() {
  return gfx::Size(kIconSize, kIconSize);
}

}  // namespace

PriceTrackingView::PriceTrackingView(Profile* profile,
                                     const GURL& page_url,
                                     bool is_price_track_enabled,
                                     const commerce::ProductInfo& product_info)
    : profile_(profile),
      is_price_track_enabled_(is_price_track_enabled),
      product_info_(product_info) {
  auto* layout_provider = views::LayoutProvider::Get();
  const int label_spacing = 0;
  const auto label_context = views::style::CONTEXT_LABEL;
  const int horizontal_spacing = layout_provider->GetDistanceMetric(
      views::DISTANCE_RELATED_CONTROL_HORIZONTAL);
  const gfx::Insets dialog_insets =
      layout_provider->GetInsetsMetric(views::INSETS_DIALOG);
  SetCrossAxisAlignment(views::LayoutAlignment::kStart);

  // Icon column
  auto* icon = AddChildView(std::make_unique<views::ImageView>());
  icon->SetImage(ui::ImageModel::FromVectorIcon(
      features::IsRoundedIconsEnabled()
          ? omnibox::kNotificationAddIcon
          : omnibox::kPriceTrackingDisabledOldIcon,
      kColorBookmarkDialogTrackPriceIcon));
  icon->SetImageSize(GetIconSize());
  icon->SetProperty(views::kMarginsKey,
                    gfx::Insets::TLBR(0, 0, 0, GetIconMargin()));

  // Text column
  auto text_container = std::make_unique<views::FlexLayoutView>();
  text_container->SetOrientation(views::LayoutOrientation::kVertical);
  // Title label
  auto* title_label =
      text_container->AddChildView(std::make_unique<views::Label>(
          l10n_util::GetStringUTF16(IDS_OMNIBOX_TRACK_PRICE_DIALOG_TITLE),
          label_context, views::style::STYLE_EMPHASIZED));
  title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  title_label->SetFocusBehavior(View::FocusBehavior::ACCESSIBLE_ONLY);

  // Body label
  int body_string_id = IDS_BOOKMARK_STAR_DIALOG_TRACK_PRICE_DESCRIPTION;

  // If "track by default" is enabled, the email consent comes after enabling
  // price tracking for the first time, so it doesn't need to be included in the
  // "email disabled" state. If we're not in the experiment, only show the
  // "email disabled" message if it is explicitly disabled (it's turned on
  // implicitly on when the first item is tracked).
  bool email_pref_set_by_user =
      commerce::IsEmailNotificationPrefSetByUser(profile_->GetPrefs());
  bool email_pref_value =
      commerce::GetEmailNotificationPrefValue(profile_->GetPrefs());

  if (!email_pref_set_by_user ||
      (email_pref_set_by_user && !email_pref_value)) {
    body_string_id = IDS_BOOKMARK_STAR_DIALOG_TRACK_PRICE_DESCRIPTION_EMAIL_OFF;
  }

  body_label_ = text_container->AddChildView(std::make_unique<views::Label>(
      l10n_util::GetStringUTF16(body_string_id), label_context,
      views::style::STYLE_SECONDARY));
  body_label_->SetFontList(body_label_->font_list().DeriveWithSizeDelta(-1));
  body_label_->SetProperty(views::kMarginsKey,
                           gfx::Insets::TLBR(label_spacing, 0, 0, 0));
  body_label_->SetMultiLine(true);
  body_label_->SetAllowCharacterBreak(true);
  body_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  body_label_->SetFocusBehavior(View::FocusBehavior::ACCESSIBLE_ONLY);
  AddChildView(std::move(text_container));

  // Toggle button column
  toggle_button_ = AddChildView(std::make_unique<views::ToggleButton>(
      base::BindRepeating(&PriceTrackingView::OnToggleButtonPressed,
                          weak_ptr_factory_.GetWeakPtr(), page_url)));

  toggle_button_->SetIsOn(is_price_track_enabled_);

  scoped_observation_.Observe(
      commerce::ShoppingServiceFactory::GetForBrowserContext(profile_));

  toggle_button_->GetViewAccessibility().SetName(GetToggleAccessibleName());
  toggle_button_->SetProperty(views::kMarginsKey,
                              gfx::Insets::TLBR(0, horizontal_spacing, 0, 0));

  const int bubble_width = ChromeLayoutProvider::Get()->GetDistanceMetric(
      views::DISTANCE_BUBBLE_PREFERRED_WIDTH);
  int label_width;
  toggle_button_->SetProperty(
      views::kFlexBehaviorKey,
      views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred,
                               views::MaximumFlexSizeRule::kPreferred)
          .WithAlignment(views::LayoutAlignment::kEnd));

  label_width = bubble_width - horizontal_spacing - dialog_insets.left() -
                dialog_insets.right() - GetIconMargin() -
                GetIconSize().width() -
                toggle_button_->GetPreferredSize().width();
  body_label_->SizeToFit(label_width);
  base::RecordAction(base::UserMetricsAction(
      "Commerce.PriceTracking.BookmarkDialogPriceTrackViewShown"));
}

PriceTrackingView::~PriceTrackingView() = default;

bool PriceTrackingView::IsToggleOn() {
  return toggle_button_->GetIsOn();
}

void PriceTrackingView::OnSubscribe(const commerce::CommerceSubscription& sub,
                                    bool succeeded) {
  if (succeeded) {
    HandleSubscriptionUpdate(sub, true);
  }
}

void PriceTrackingView::OnUnsubscribe(const commerce::CommerceSubscription& sub,
                                      bool succeeded) {
  if (succeeded) {
    HandleSubscriptionUpdate(sub, false);
  }
}

void PriceTrackingView::HandleSubscriptionUpdate(
    const commerce::CommerceSubscription& sub,
    bool is_tracking) {
  if (sub.id_type == commerce::IdentifierType::kProductClusterId &&
      base::NumberToString(product_info_.product_cluster_id.value_or(
          commerce::kInvalidSubscriptionId)) == sub.id) {
    is_price_track_enabled_ = is_tracking;
    toggle_button_->SetIsOn(is_tracking);
    toggle_button_->GetViewAccessibility().SetName(GetToggleAccessibleName());
  }
}

std::u16string PriceTrackingView::GetToggleAccessibleName() {
  return l10n_util::GetStringUTF16(
      IDS_PRICE_TRACKING_TRACK_PRODUCT_ACCESSIBILITY);
}

void PriceTrackingView::OnToggleButtonPressed(const GURL& url) {
  is_price_track_enabled_ = !is_price_track_enabled_;
  if (is_price_track_enabled_) {
    base::RecordAction(base::UserMetricsAction(
        "Commerce.PriceTracking.BookmarkDialogPriceTrackViewTrackedPrice"));
  } else {
    base::RecordAction(base::UserMetricsAction(
        "Commerce.PriceTracking.BookmarkDialogPriceTrackViewUntrackedPrice"));
  }

  toggle_button_->GetViewAccessibility().SetName(GetToggleAccessibleName());
  UpdatePriceTrackingState(url);
}

void PriceTrackingView::UpdatePriceTrackingState(const GURL& url) {
  bookmarks::BookmarkModel* const model =
      BookmarkModelFactory::GetForBrowserContext(profile_);
  const bookmarks::BookmarkNode* node =
      model->GetMostRecentlyAddedUserNodeForURL(url);

  // If "track by default" is on, we'll show a dialog after saving to offer
  // email notifications.
  if (profile_ && is_price_track_enabled_) {
    commerce::MaybeEnableEmailNotifications(profile_->GetPrefs());
  }

  commerce::ShoppingService* service =
      commerce::ShoppingServiceFactory::GetForBrowserContext(profile_);
  base::OnceCallback<void(bool)> callback =
      base::BindOnce(&PriceTrackingView::OnPriceTrackingStateUpdated,
                     weak_ptr_factory_.GetWeakPtr());

  if (node) {
    commerce::SetPriceTrackingStateForBookmark(
        service, model, node, is_price_track_enabled_, std::move(callback));
  } else {
    DCHECK(!is_price_track_enabled_);
    if (commerce::CanTrackPrice(product_info_)) {
      commerce::SetPriceTrackingStateForClusterId(
          service, model, product_info_.product_cluster_id.value(),
          is_price_track_enabled_, std::move(callback));
    }
  }
}

void PriceTrackingView::OnPriceTrackingStateUpdated(bool success) {
  // TODO(crbug.com/40232577): Record latency for the update status.
  if (!success) {
    is_price_track_enabled_ = !is_price_track_enabled_;
    toggle_button_->SetIsOn(is_price_track_enabled_);
    toggle_button_->GetViewAccessibility().SetName(GetToggleAccessibleName());
    body_label_->SetText(l10n_util::GetStringUTF16(
        IDS_OMNIBOX_TRACK_PRICE_DIALOG_ERROR_DESCRIPTION));
  }
}

BEGIN_METADATA(PriceTrackingView)
END_METADATA
