// Copyright 2019 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/extensions/extensions_menu_test_util.h"

#include <algorithm>

#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/extensions/extension_action_view_model.h"
#include "chrome/browser/ui/toolbar/toolbar_action_view_model.h"
#include "chrome/browser/ui/views/controls/hover_button.h"
#include "chrome/browser/ui/views/extensions/extension_popup.h"
#include "chrome/browser/ui/views/extensions/extensions_menu_button.h"
#include "chrome/browser/ui/views/extensions/extensions_menu_coordinator.h"
#include "chrome/browser/ui/views/extensions/extensions_menu_delegate_desktop.h"
#include "chrome/browser/ui/views/extensions/extensions_menu_entry_view.h"
#include "chrome/browser/ui/views/extensions/extensions_menu_item_view.h"
#include "chrome/browser/ui/views/extensions/extensions_menu_main_page_view.h"
#include "chrome/browser/ui/views/extensions/extensions_menu_view.h"
#include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h"
#include "chrome/browser/ui/views/extensions/extensions_toolbar_coordinator.h"
#include "chrome/browser/ui/views/extensions/extensions_toolbar_desktop.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "extensions/common/extension_features.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/layout/animating_layout_manager_test_util.h"
#include "ui/views/style/platform_style.h"
#include "ui/views/test/button_test_api.h"
#include "ui/views/view.h"
#include "ui/views/view_utils.h"

#if BUILDFLAG(IS_OZONE)
#include "ui/ozone/public/ozone_platform.h"
#endif

ExtensionsMenuTestUtil::ExtensionsMenuTestUtil(BrowserWindowInterface* browser)
    : scoped_allow_extensions_menu_instances_(
          ExtensionsMenuView::AllowInstancesForTesting()),
      browser_(browser) {
  extensions_toolbar_ = BrowserView::GetBrowserViewForBrowser(browser_)
                            ->toolbar()
                            ->extensions_container();
}

ExtensionsMenuTestUtil::~ExtensionsMenuTestUtil() {
  if (!IsExtensionsMenuShowing()) {
    return;
  }

  if (base::FeatureList::IsEnabled(
          extensions_features::kExtensionsMenuAccessControl)) {
    extensions_toolbar_->GetExtensionsMenuCoordinatorForTesting()->Hide();
  } else {
    menu_view_->GetWidget()->CloseNow();
  }
}

void ExtensionsMenuTestUtil::OnViewIsDeleting(views::View* observed_view) {
  CHECK_EQ(observed_view, menu_view_);
  menu_view_ = nullptr;
}

int ExtensionsMenuTestUtil::NumberOfBrowserActions() {
  return extensions_toolbar_->GetNumberOfActionsForTesting();
}

bool ExtensionsMenuTestUtil::HasAction(const extensions::ExtensionId& id) {
  return extensions_toolbar_->GetToolbarViewModel()->GetActionForId(id) !=
         nullptr;
}

void ExtensionsMenuTestUtil::InspectPopup(const extensions::ExtensionId& id) {
  auto* view_model = static_cast<ExtensionActionViewModel*>(
      extensions_toolbar_->GetToolbarViewModel()->GetActionForId(id));
  DCHECK(view_model);
  view_model->InspectPopup();
}

gfx::Image ExtensionsMenuTestUtil::GetIcon(const extensions::ExtensionId& id) {
  ToolbarActionView* action_view = extensions_toolbar_->GetViewForId(id);
  DCHECK(action_view);
  return gfx::Image(action_view->GetIconForTest());
}

void ExtensionsMenuTestUtil::Press(const extensions::ExtensionId& id) {
  OpenExtensionsMenu();

  HoverButton* action_button = GetActionButton(id);
  CHECK(action_button);

  views::test::ButtonTestApi(action_button).NotifyDefaultMouseClick();
}

gfx::NativeView ExtensionsMenuTestUtil::GetPopupNativeView() {
  ToolbarActionViewModel* popup_owner =
      extensions_toolbar_->popup_owner_for_testing();
  return popup_owner ? popup_owner->GetPopupNativeViewForTesting()
                     : gfx::NativeView();
}

bool ExtensionsMenuTestUtil::HasPopup() {
  return !!GetPopupNativeView();
}

bool ExtensionsMenuTestUtil::HidePopup() {
  // ExtensionsToolbarDesktop::HideActivePopup() is private. Get around it by
  // casting to an ExtensionsContainer.
  static_cast<ExtensionsContainer*>(extensions_toolbar_->GetToolbarViewModel())
      ->HideActivePopup();
  return !HasPopup();
}

void ExtensionsMenuTestUtil::WaitForExtensionsContainerLayout() {
  views::test::WaitForAnimatingLayoutManager(
      static_cast<views::View*>(extensions_toolbar_));
}

gfx::Size ExtensionsMenuTestUtil::GetMinPopupSize() {
  return ExtensionPopup::kMinSize;
}

gfx::Size ExtensionsMenuTestUtil::GetMaxPopupSize() {
  return ExtensionPopup::kMaxSize;
}

gfx::Size ExtensionsMenuTestUtil::GetMaxAvailableSizeToFitBubbleOnScreen(
    const extensions::ExtensionId& id) {
#if BUILDFLAG(IS_OZONE)
  if (!ui::OzonePlatform::GetInstance()
           ->GetPlatformProperties()
           .supports_global_screen_coordinates) {
    return ExtensionPopup::kMaxSize;
  }
#endif
  return views::BubbleDialogDelegate::GetMaxAvailableScreenSpaceToPlaceBubble(
      extensions_toolbar_->GetReferenceButtonForPopup(id),
      views::BubbleBorder::TOP_RIGHT,
      views::PlatformStyle::kAdjustBubbleIfOffscreen,
      views::BubbleFrameView::PreferredArrowAdjustment::kMirror);
}

void ExtensionsMenuTestUtil::OpenExtensionsMenu() {
  if (IsExtensionsMenuShowing()) {
    return;
  }

  std::unique_ptr<views::BubbleDialogDelegate> bubble_dialog;
  if (base::FeatureList::IsEnabled(
          extensions_features::kExtensionsMenuAccessControl)) {
    bubble_dialog =
        extensions_toolbar_->GetExtensionsMenuCoordinatorForTesting()
            ->CreateExtensionsMenuBubbleDialogDelegateForTesting(
                views::BubbleAnchor(extensions_toolbar_->GetExtensionsButton()),
                extensions_toolbar_);
  } else {
    bubble_dialog = std::make_unique<ExtensionsMenuView>(
        views::BubbleAnchor(extensions_toolbar_->GetExtensionsButton()),
        browser_, extensions_toolbar_->GetToolbarViewModel(),
        extensions_toolbar_);
    menu_view_ = views::AsViewClass<ExtensionsMenuView>(
        bubble_dialog->GetContentsView());

    menu_view_->View::AddObserver(this);
  }

  views::BubbleDialogDelegate::CreateBubbleDeprecated(
      std::move(bubble_dialog),
      views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET);
}

bool ExtensionsMenuTestUtil::IsExtensionsMenuShowing() {
  return base::FeatureList::IsEnabled(
             extensions_features::kExtensionsMenuAccessControl)
             ? extensions_toolbar_->GetExtensionsMenuCoordinatorForTesting()
                   ->IsShowing()
             : menu_view_;
}

HoverButton* ExtensionsMenuTestUtil::GetActionButton(
    const extensions::ExtensionId& id) {
  if (base::FeatureList::IsEnabled(
          extensions_features::kExtensionsMenuAccessControl)) {
    ExtensionsMenuMainPageView* main_page =
        extensions_toolbar_->GetExtensionsMenuCoordinatorForTesting()
            ->GetDelegateForTesting()
            ->GetMainPageViewForTesting();
    DCHECK(main_page);
    std::vector<ExtensionsMenuEntryView*> menu_entries =
        main_page->GetMenuEntries();

    auto iter = std::find_if(menu_entries.begin(), menu_entries.end(),
                             [&id](ExtensionsMenuEntryView* entry) {
                               return entry->extension_id() == id;
                             });

    return (iter == menu_entries.end()) ? nullptr
                                        : (*iter)->action_button_for_testing();
  }

  base::flat_set<raw_ptr<ExtensionMenuItemView, CtnExperimental>> menu_items =
      menu_view_->extensions_menu_items_for_testing();
  auto iter = std::find_if(menu_items.begin(), menu_items.end(),
                           [&id](ExtensionMenuItemView* item) {
                             return item->view_model()->GetId() == id;
                           });

  return (iter == menu_items.end())
             ? nullptr
             : (*iter)->primary_action_button_for_testing();
}

// static
std::unique_ptr<ExtensionActionTestHelper> ExtensionActionTestHelper::Create(
    BrowserWindowInterface* browser) {
  return std::make_unique<ExtensionsMenuTestUtil>(browser);
}
