// 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/views/incognito_clear_browsing_data_dialog.h"

#include "base/memory/raw_ptr.h"
#include "base/test/metrics/histogram_tester.h"
#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
#include "chrome/browser/ui/test/test_browser_dialog.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
#include "chrome/browser/ui/views/incognito_clear_browsing_data_dialog_coordinator.h"
#include "chrome/browser/ui/views/toolbar/avatar_toolbar_button_interface.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/test/browser_test.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/widget/widget_observer.h"

namespace {

class IncognitoClearBrowsingDataDialogTest : public InProcessBrowserTest {
 public:
  void OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type type) {
    if (!incognito_browser_) {
      incognito_browser_ = CreateIncognitoBrowser(GetProfile());
    }

    auto* coordinator = GetCoordinator();
    BrowserView* browser_view =
        BrowserView::GetBrowserViewForBrowser(incognito_browser_);
    views::BubbleAnchor anchor = browser_view->toolbar_button_provider()
                                     ->GetAvatarToolbarButtonInterface()
                                     ->GetBubbleAnchor(*incognito_browser_);
    coordinator->Show(type, anchor);
    EXPECT_TRUE(coordinator->IsShowing());
  }

  BrowserWindowInterface* GetIncognitoBrowser() { return incognito_browser_; }

  IncognitoClearBrowsingDataDialog* GetDialogView() {
    return GetCoordinator()->GetIncognitoClearBrowsingDataDialogForTesting();
  }

  views::Widget* GetDialogWidget() {
    auto* dialog_view = GetDialogView();
    return dialog_view ? dialog_view->GetWidget() : nullptr;
  }

  IncognitoClearBrowsingDataDialogCoordinator* GetCoordinator() {
    return incognito_browser_->GetFeatures()
        .incognito_clear_browsing_data_dialog_coordinator();
  }

 private:
  raw_ptr<BrowserWindowInterface, AcrossTasksDanglingUntriaged>
      incognito_browser_ = nullptr;
};

// Used to test that the bubble widget is destroyed before the host browser.
class BubbleWidgetDestroyedObserver : public views::WidgetObserver {
 public:
  BubbleWidgetDestroyedObserver(BrowserWindowInterface* host_browser,
                                views::Widget* bubble_widget)
      : host_browser_(host_browser->GetWeakPtr()) {
    bubble_widget->AddObserver(this);
  }
  ~BubbleWidgetDestroyedObserver() override = default;

  // views::WidgetObserver:
  void OnWidgetDestroyed(views::Widget* widget) override {
    ASSERT_TRUE(host_browser_);
  }

 private:
  base::WeakPtr<BrowserWindowInterface> host_browser_;
};

}  // namespace

class IncognitoClearBrowsingDataDialogBrowserTest
    : public SupportsTestDialog<IncognitoClearBrowsingDataDialogTest> {
 public:
  void ShowUi(const std::string& name) override {
    OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::kDefaultBubble);
  }
};

IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogBrowserTest,
                       InvokeUi_default) {
  ShowAndVerifyUi();
}

IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogTest,
                       TestDialogIsShown_DefaultBubble) {
  OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::kDefaultBubble);
  auto* incognito_cbd_dialog_view = GetDialogView();

  ASSERT_TRUE(GetCoordinator()->IsShowing());
  ASSERT_EQ(static_cast<int>(ui::mojom::DialogButton::kOk) |
                static_cast<int>(ui::mojom::DialogButton::kCancel),
            incognito_cbd_dialog_view->buttons());
  ASSERT_TRUE(incognito_cbd_dialog_view->IsDialogButtonEnabled(
      ui::mojom::DialogButton::kOk));
  ASSERT_TRUE(incognito_cbd_dialog_view->IsDialogButtonEnabled(
      ui::mojom::DialogButton::kCancel));
}

IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogTest,
                       TestCloseWindowsButton) {
  base::HistogramTester histogram_tester;
  OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::kDefaultBubble);
  auto destroyed_observer = BubbleWidgetDestroyedObserver(
      GetIncognitoBrowser(), GetDialogView()->GetWidget());

  ui_test_utils::BrowserDestroyedObserver observer(GetIncognitoBrowser());
  GetDialogView()->AcceptDialog();
  histogram_tester.ExpectBucketCount(
      "Incognito.ClearBrowsingDataDialog.ActionType",
      IncognitoClearBrowsingDataDialog::DialogActionType::kCloseIncognito, 1);
  observer.Wait();
  ASSERT_EQ(0u,
            GlobalBrowserCollection::GetInstance()->GetIncognitoBrowserCount());
}

IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogTest, TestCancelButton) {
  base::HistogramTester histogram_tester;
  OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::kDefaultBubble);

  views::test::WidgetDestroyedWaiter destroyed_waiter(GetDialogWidget());

  GetDialogView()->Cancel();
  histogram_tester.ExpectBucketCount(
      "Incognito.ClearBrowsingDataDialog.ActionType",
      IncognitoClearBrowsingDataDialog::DialogActionType::kCancel, 1);
  destroyed_waiter.Wait();

  ASSERT_FALSE(GetCoordinator()->IsShowing());
  ASSERT_FALSE(GetDialogView());
}

IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogTest,
                       TestBrowserCloseEventClosesDialogFirst) {
  OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::kDefaultBubble);
  auto destroyed_observer =
      BubbleWidgetDestroyedObserver(GetIncognitoBrowser(), GetDialogWidget());

  CloseBrowserSynchronously(GetIncognitoBrowser());

  ASSERT_EQ(0u,
            GlobalBrowserCollection::GetInstance()->GetIncognitoBrowserCount());
}

IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogTest,
                       ClearBrowsingDataNavigationInIncognito) {
  BrowserWindowInterface* incognito_browser = CreateIncognitoBrowser();
  ui_test_utils::SendToOmniboxAndSubmit(incognito_browser,
                                        "chrome://settings/clearBrowserData");
  std::u16string current_tab_title;
  ui_test_utils::GetCurrentTabTitle(incognito_browser, &current_tab_title);
  EXPECT_EQ(u"about:blank", current_tab_title);
  auto* coordinator = incognito_browser->GetFeatures()
                          .incognito_clear_browsing_data_dialog_coordinator();
  ASSERT_TRUE(coordinator->IsShowing());
}

IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogTest,
                       TestDialogIsShown_HistoryDisclaimerBubble) {
  OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::
                 kHistoryDisclaimerBubble);
  auto* incognito_cbd_dialog_view = GetDialogView();

  ASSERT_TRUE(GetCoordinator()->IsShowing());
  ASSERT_EQ(static_cast<int>(ui::mojom::DialogButton::kOk) |
                static_cast<int>(ui::mojom::DialogButton::kCancel),
            incognito_cbd_dialog_view->buttons());
  ASSERT_TRUE(incognito_cbd_dialog_view->IsDialogButtonEnabled(
      ui::mojom::DialogButton::kOk));
  ASSERT_TRUE(incognito_cbd_dialog_view->IsDialogButtonEnabled(
      ui::mojom::DialogButton::kCancel));
}

IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogTest,
                       TestCloseIncognitoButton) {
  OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::
                 kHistoryDisclaimerBubble);

  ui_test_utils::BrowserDestroyedObserver observer(GetIncognitoBrowser());
  GetDialogView()->CancelDialog();
  observer.Wait();
  ASSERT_EQ(0u,
            GlobalBrowserCollection::GetInstance()->GetIncognitoBrowserCount());
}

IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogTest, TestGotItButton) {
  OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::
                 kHistoryDisclaimerBubble);

  views::test::WidgetDestroyedWaiter destroyed_waiter(GetDialogWidget());

  GetDialogView()->AcceptDialog();
  destroyed_waiter.Wait();

  ASSERT_FALSE(GetCoordinator()->IsShowing());
  ASSERT_FALSE(GetDialogView());
}

IN_PROC_BROWSER_TEST_F(IncognitoClearBrowsingDataDialogTest, ShowTwice) {
  OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::
                 kHistoryDisclaimerBubble);
  auto* const first_incognito_cbd_dialog_view = GetDialogView();
  EXPECT_TRUE(first_incognito_cbd_dialog_view);

  // `view_tracker_for_first_view` is used to check if
  // `first_incognito_cbd_dialog_view` has been released.
  views::ViewTracker view_tracker_for_first_view(
      first_incognito_cbd_dialog_view);

  // Show again with the same type.
  OpenDialog(IncognitoClearBrowsingDataDialogInterface::Type::
                 kHistoryDisclaimerBubble);
  const auto* const second_incognito_cbd_dialog_view = GetDialogView();
  EXPECT_TRUE(second_incognito_cbd_dialog_view);

  // Two views should not be the same.
  EXPECT_NE(first_incognito_cbd_dialog_view, second_incognito_cbd_dialog_view);

  views::test::WidgetDestroyedWaiter destroyed_waiter(GetDialogWidget());

  GetDialogView()->AcceptDialog();
  destroyed_waiter.Wait();

  // `first_incognito_cbd_dialog_view` should already have been released when
  // the second dialog is created.
  EXPECT_FALSE(view_tracker_for_first_view.view());

  ASSERT_FALSE(GetCoordinator()->IsShowing());
  ASSERT_FALSE(GetDialogView());
}
