// Copyright 2021 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/extensions/extension_install_friction_dialog.h"

#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "chrome/browser/ui/extensions/extension_dialog_utils.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "components/strings/grit/components_strings.h"
#include "components/vector_icons/vector_icons.h"
#include "content/public/browser/web_contents.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/common/constants.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/dialog_model.h"
#include "ui/base/ui_base_features.h"
#include "ui/color/color_id.h"

static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));

namespace {

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class ExtensionInstallFrictionDialogAction {
  kClose = 0,
  kLearnMore = 1,
  kContinueToInstall = 2,
  kClosedWithoutUserAction = 3,
  kMaxValue = kClosedWithoutUserAction,
};

void ReportExtensionInstallFrictionDialogAction(
    ExtensionInstallFrictionDialogAction action) {
  base::UmaHistogramEnumeration("Extensions.InstallFrictionDialogAction",
                                action);
}

class ExtensionInstallFrictionDialogDelegate : public ui::DialogModelDelegate {
 public:
  explicit ExtensionInstallFrictionDialogDelegate(
      content::WebContents* web_contents,
      base::OnceCallback<void(bool)> callback)
      : original_web_contents_(web_contents->GetWeakPtr()),
        callback_(std::move(callback)) {}

  ~ExtensionInstallFrictionDialogDelegate() override = default;

  void OnDialogAccepted() {
    ReportExtensionInstallFrictionDialogAction(
        ExtensionInstallFrictionDialogAction::kContinueToInstall);
    std::move(callback_).Run(true);
  }

  void OnDialogCanceled() {
    ExtensionInstallFrictionDialogAction dialog_action =
        learn_more_clicked_ ? ExtensionInstallFrictionDialogAction::kLearnMore
                            : ExtensionInstallFrictionDialogAction::kClose;
    ReportExtensionInstallFrictionDialogAction(dialog_action);
    std::move(callback_).Run(false);
  }

  void OnDialogDestroyed() {
    if (callback_) {
      // The dialog may close without firing any of the [accept | cancel |
      // close] callbacks if e.g. the parent window closes. In this case, we
      // have to manually run the callback.
      ReportExtensionInstallFrictionDialogAction(
          ExtensionInstallFrictionDialogAction::kClosedWithoutUserAction);
      std::move(callback_).Run(false);
    }
  }

  void OnLearnMoreLinkClicked() {
    learn_more_clicked_ = true;

    if (original_web_contents_) {
      GURL url(chrome::kCwsEnhancedSafeBrowsingLearnMoreURL);
      content::OpenURLParams params(
          url, content::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB,
          ui::PAGE_TRANSITION_LINK, /*is_renderer_initiated=*/false);
      original_web_contents_->OpenURL(params, {});
    }

    dialog_model()->host()->Close();
  }

 private:
  base::WeakPtr<content::WebContents> original_web_contents_;
  base::OnceCallback<void(bool)> callback_;
  bool learn_more_clicked_ = false;
};

}  // namespace

namespace extensions {

DEFINE_ELEMENT_IDENTIFIER_VALUE(kExtensionInstallFrictionLearnMoreLink);

void ShowExtensionInstallFrictionDialog(
    content::WebContents* web_contents,
    base::OnceCallback<void(bool)> callback) {
  auto dialog_delegate_unique =
      std::make_unique<ExtensionInstallFrictionDialogDelegate>(
          web_contents, std::move(callback));
  ExtensionInstallFrictionDialogDelegate* dialog_delegate =
      dialog_delegate_unique.get();

  std::unique_ptr<ui::DialogModel> dialog =
      ui::DialogModel::Builder(std::move(dialog_delegate_unique))
          .SetTitle(l10n_util::GetStringUTF16(
              IDS_EXTENSION_PROMPT_INSTALL_FRICTION_TITLE))
          .SetIcon(ui::ImageModel::FromVectorIcon(
              features::IsRoundedIconsEnabled()
                  ? vector_icons::kGppMaybeIcon
                  : vector_icons::kGppMaybeOldIcon,
              ui::kColorAlertMediumSeverityIcon,
              extension_misc::EXTENSION_ICON_SMALLISH))
          .AddOkButton(
              base::BindOnce(
                  &ExtensionInstallFrictionDialogDelegate::OnDialogAccepted,
                  base::Unretained(dialog_delegate)),
              ui::DialogModel::Button::Params().SetLabel(
                  l10n_util::GetStringUTF16(
                      IDS_EXTENSION_PROMPT_INSTALL_FRICTION_CONTINUE_BUTTON)))
          .AddCancelButton(
              base::BindOnce(
                  &ExtensionInstallFrictionDialogDelegate::OnDialogCanceled,
                  base::Unretained(dialog_delegate)),
              ui::DialogModel::Button::Params().SetLabel(
                  l10n_util::GetStringUTF16(IDS_CLOSE)))
          .SetCloseActionCallback(base::BindOnce(
              &ExtensionInstallFrictionDialogDelegate::OnDialogCanceled,
              base::Unretained(dialog_delegate)))
          .SetDialogDestroyingCallback(base::BindOnce(
              &ExtensionInstallFrictionDialogDelegate::OnDialogDestroyed,
              base::Unretained(dialog_delegate)))
          .OverrideDefaultButton(ui::mojom::DialogButton::kCancel)
          .AddParagraph(ui::DialogModelLabel::CreateWithReplacement(
                            IDS_EXTENSION_PROMPT_INSTALL_FRICTION_WARNING_TEXT,
                            ui::DialogModelLabel::CreateLink(
                                IDS_LEARN_MORE,
                                base::BindRepeating(
                                    &ExtensionInstallFrictionDialogDelegate::
                                        OnLearnMoreLinkClicked,
                                    base::Unretained(dialog_delegate)))),
                        /*header=*/std::u16string(),
                        /*id=*/kExtensionInstallFrictionLearnMoreLink)
          .Build();

  ShowWebModalDialog(web_contents, std::move(dialog));
}

}  // namespace extensions
