// 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/views/location_bar/cookie_controls/cookie_controls_content_view.h"

#include "base/strings/string_util.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/controls/rich_controls_container_view.h"
#include "chrome/browser/ui/views/controls/rich_hover_button.h"
#include "chrome/grit/generated_resources.h"
#include "components/content_settings/browser/ui/cookie_controls_util.h"
#include "components/content_settings/core/common/features.h"
#include "components/privacy_sandbox/privacy_sandbox_features.h"
#include "components/vector_icons/vector_icons.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/controls/button/toggle_button.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/separator.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/box_layout_view.h"
#include "ui/views/vector_icons.h"
#include "ui/views/view.h"
#include "ui/views/view_class_properties.h"

namespace {

using Util = ::content_settings::CookieControlsUtil;

constexpr int kMaxBubbleWidth = 1000;

int GetDefaultIconSize() {
  return GetLayoutConstant(LayoutConstant::kPageInfoIconSize);
}

std::unique_ptr<views::View> CreateSeparator(bool padded) {
  int vmargin = ChromeLayoutProvider::Get()->GetDistanceMetric(
      DISTANCE_CONTENT_LIST_VERTICAL_MULTI);
  int hmargin = padded
                    ? ChromeLayoutProvider::Get()->GetDistanceMetric(
                          DISTANCE_HORIZONTAL_SEPARATOR_PADDING_PAGE_INFO_VIEW)
                    : 0;

  auto separator = std::make_unique<views::Separator>();
  separator->SetProperty(views::kMarginsKey, gfx::Insets::VH(vmargin, hmargin));
  return separator;
}

std::unique_ptr<views::View> CreateFullWidthSeparator() {
  return CreateSeparator(/*padded=*/false);
}

gfx::Insets UserBypassBubbleInsets() {
  auto* provider = ChromeLayoutProvider::Get();
  const int vertical_margin =
      provider->GetDistanceMetric(DISTANCE_CONTENT_LIST_VERTICAL_MULTI);
  const int side_margin =
      provider->GetInsetsMetric(views::INSETS_DIALOG).left();
  return gfx::Insets::VH(vertical_margin, side_margin);
}

}  // namespace

DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(CookieControlsContentView, kTitle);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(CookieControlsContentView, kDescription);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(CookieControlsContentView,
                                      kThirdPartyCookiesSummary);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(CookieControlsContentView, kToggleButton);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(CookieControlsContentView, kToggleLabel);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(CookieControlsContentView,
                                      kThirdPartyCookiesLabel);

CookieControlsContentView::CookieControlsContentView() {
  SetLayoutManager(std::make_unique<views::BoxLayout>(
      views::BoxLayout::Orientation::kVertical));
  AddChildView(CreateFullWidthSeparator());
  AddContentLabels();
  AddToggleRow();
}

void CookieControlsContentView::AddContentLabels() {
  label_wrapper_ = AddChildView(std::make_unique<views::View>());
  label_wrapper_->SetLayoutManager(std::make_unique<views::BoxLayout>(
      views::BoxLayout::Orientation::kVertical));
  label_wrapper_->SetProperty(views::kMarginsKey, UserBypassBubbleInsets());
  title_ = label_wrapper_->AddChildView(std::make_unique<views::Label>());
  title_->SetTextContext(views::style::CONTEXT_DIALOG_BODY_TEXT);
  title_->SetTextStyle(views::style::STYLE_BODY_3_EMPHASIS);
  title_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
  title_->SetProperty(views::kElementIdentifierKey, kTitle);

  description_ = label_wrapper_->AddChildView(std::make_unique<views::Label>());
  description_->SetTextContext(views::style::CONTEXT_LABEL);
  description_->SetTextStyle(views::style::STYLE_BODY_5);
  description_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
  description_->SetMultiLine(true);
  description_->SetProperty(views::kElementIdentifierKey, kDescription);
}

void CookieControlsContentView::SetToggleIsOn(bool is_on) {
  toggle_button_->SetIsOn(is_on);
}

void CookieControlsContentView::SetToggleIcon(const gfx::VectorIcon& icon) {
  cookies_row_->SetIcon(ui::ImageModel::FromVectorIcon(icon, ui::kColorIcon,
                                                       GetDefaultIconSize()));
}

void CookieControlsContentView::SetToggleVisible(bool visible) {
  toggle_button_->SetVisible(visible);
}

void CookieControlsContentView::SetCookiesLabel(const std::u16string& label) {
  cookies_label_->SetText(label);
  cookies_label_->SetTextStyle(views::style::STYLE_BODY_5);
  cookies_label_->SetProperty(views::kElementIdentifierKey, kToggleLabel);

  const std::u16string accessible_name = base::JoinString(
      {
          l10n_util::GetStringUTF16(
              IDS_COOKIE_CONTROLS_BUBBLE_THIRD_PARTY_COOKIES_LABEL),
          label,
      },
      u"\n");
  toggle_button_->GetViewAccessibility().SetName(accessible_name);
}

void CookieControlsContentView::SetEnforcedIcon(const gfx::VectorIcon& icon,
                                                const std::u16string& tooltip) {
  enforced_icon_->SetImage(ui::ImageModel::FromVectorIcon(
      icon, ui::kColorIcon, GetDefaultIconSize()));
  enforced_icon_->SetTooltipText(tooltip);
}

void CookieControlsContentView::SetEnforcedIconVisible(bool visible) {
  if (enforced_icon_ != nullptr) {
    enforced_icon_->SetVisible(visible);
  }
}

void CookieControlsContentView::SetCookiesRowVisible(bool visible) {
  cookies_row_->SetVisible(visible);
}

void CookieControlsContentView::AddToggleRow() {
  cookies_row_ = AddChildView(std::make_unique<RichControlsContainerView>());
  cookies_row_->SetTitle(l10n_util::GetStringUTF16(
      IDS_COOKIE_CONTROLS_BUBBLE_THIRD_PARTY_COOKIES_LABEL));

  // The label will be provided via SetCookiesLabel().
  cookies_label_ = cookies_row_->AddSecondaryLabel(u"");
  enforced_icon_ =
      cookies_row_->AddControl(std::make_unique<views::ImageView>());

  toggle_button_ = cookies_row_->AddControl(
      std::make_unique<views::ToggleButton>(base::BindRepeating(
          &CookieControlsContentView::NotifyToggleButtonPressedCallback,
          base::Unretained(this))));
  toggle_button_->SetPreferredSize(
      gfx::Size(toggle_button_->GetPreferredSize().width(),
                cookies_row_->GetFirstLineHeight()));
  toggle_button_->GetViewAccessibility().SetName(l10n_util::GetStringUTF16(
      IDS_COOKIE_CONTROLS_BUBBLE_THIRD_PARTY_COOKIES_LABEL));
  toggle_button_->SetVisible(true);
  toggle_button_->SetProperty(views::kElementIdentifierKey, kToggleButton);
}

void CookieControlsContentView::UpdateContentLabels(
    const std::u16string& title,
    const std::u16string& description) {
  title_->SetText(title);
  description_->SetText(description);
}

void CookieControlsContentView::SetContentLabelsVisible(bool visible) {
  // Set visibility on the wrapper to ensure that margins are correctly updated.
  label_wrapper_->SetVisible(visible);
}

CookieControlsContentView::~CookieControlsContentView() = default;

void CookieControlsContentView::PreferredSizeChanged() {
  views::View::PreferredSizeChanged();
}

gfx::Size CookieControlsContentView::CalculatePreferredSize(
    const views::SizeBounds& available_size) const {
  // Ensure that the width is only increased to support a longer title string,
  // or a longer toggle. Other information can be wrapped or elided to keep
  // the standard size.
  auto size = views::View::CalculatePreferredSize(available_size);

  auto* provider = ChromeLayoutProvider::Get();
  const int margins = provider->GetInsetsMetric(views::INSETS_DIALOG).width();

  int title_width = title_->GetPreferredSize().width() + margins;

  int desired_width = std::clamp(
      std::max(title_width, cookies_row_->GetPreferredSize().width()),
      ChromeLayoutProvider::Get()->GetDistanceMetric(
          views::DistanceMetric::DISTANCE_BUBBLE_PREFERRED_WIDTH),
      kMaxBubbleWidth);

  return gfx::Size(desired_width, size.height());
}

base::CallbackListSubscription
CookieControlsContentView::RegisterToggleButtonPressedCallback(
    base::RepeatingCallback<void(bool)> callback) {
  return toggle_button_callback_list_.Add(std::move(callback));
}

void CookieControlsContentView::NotifyToggleButtonPressedCallback() {
  toggle_button_callback_list_.Notify(toggle_button_->GetIsOn());
}

BEGIN_METADATA(CookieControlsContentView)
END_METADATA
