// Copyright 2026 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/user_education/browser_help_bubble.h"

#include <memory>
#include <vector>

#include "base/functional/bind.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/user_education/views/help_bubble_delegate.h"
#include "components/user_education/webui/help_bubble_handler.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/web_contents_tester.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/webui/resources/js/tracked_element/tracked_element.mojom-forward.h"
#include "ui/webui/resources/js/tracked_element/tracked_element.mojom.h"
#include "ui/webui/tracked_element/tracked_element_handler.h"
#include "ui/webui/tracked_element/tracked_element_web_ui.h"

namespace {

DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestElementId);
constexpr std::string_view kTestElementSecondaryId = "3";

class MockHelpBubbleClient : public help_bubble::mojom::HelpBubbleClient {
 public:
  MOCK_METHOD(void,
              ShowHelpBubble,
              (help_bubble::mojom::HelpBubbleParamsPtr data),
              (override));
  MOCK_METHOD(void,
              ToggleFocusForAccessibility,
              (tracked_element::mojom::TrackedElementIdentifierPtr),
              (override));
  MOCK_METHOD(void,
              HideHelpBubble,
              (tracked_element::mojom::TrackedElementIdentifierPtr),
              (override));
  MOCK_METHOD(void,
              ExternalHelpBubbleUpdated,
              (tracked_element::mojom::TrackedElementIdentifierPtr, bool shown),
              (override));
};

class TestHelpBubbleHandler : public user_education::HelpBubbleHandlerBase {
 public:
  explicit TestHelpBubbleHandler(
      base::WeakPtr<ui::TrackedElementHandler> tracked_element_handler)
      : HelpBubbleHandlerBase(std::make_unique<ClientProvider>(),
                              tracked_element_handler) {}

 private:
  class ClientProvider : public HelpBubbleHandlerBase::ClientProvider {
   public:
    help_bubble::mojom::HelpBubbleClient* GetClient() override {
      return &client_;
    }

   private:
    testing::NiceMock<MockHelpBubbleClient> client_;
  };
};

class MockHelpBubbleDelegate : public user_education::HelpBubbleDelegate {
 public:
  MOCK_METHOD(std::vector<ui::Accelerator>,
              GetPaneNavigationAccelerators,
              (ui::TrackedElement*),
              (const, override));
  MOCK_METHOD(int, GetTitleTextContext, (), (const, override));
  MOCK_METHOD(int, GetBodyTextContext, (), (const, override));
  MOCK_METHOD(ui::ColorId,
              GetHelpBubbleBackgroundColorId,
              (),
              (const, override));
  MOCK_METHOD(ui::ColorId,
              GetHelpBubbleForegroundColorId,
              (),
              (const, override));
  MOCK_METHOD(ui::ColorId,
              GetHelpBubbleDefaultButtonBackgroundColorId,
              (),
              (const, override));
  MOCK_METHOD(ui::ColorId,
              GetHelpBubbleDefaultButtonForegroundColorId,
              (),
              (const, override));
  MOCK_METHOD(ui::ColorId,
              GetHelpBubbleButtonBorderColorId,
              (),
              (const, override));
  MOCK_METHOD(ui::ColorId,
              GetHelpBubbleCloseButtonInkDropColorId,
              (),
              (const, override));
};

}  // namespace

class BrowserHelpBubbleUnitTest : public ChromeRenderViewHostTestHarness {
 public:
  BrowserHelpBubbleUnitTest() = default;
  ~BrowserHelpBubbleUnitTest() override = default;

  void SetUp() override {
    ChromeRenderViewHostTestHarness::SetUp();
    web_contents_ =
        content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
    ui::ElementContext context =
        ui::ElementContext::CreateFakeContextForTesting(this);
    tracked_element_handler_ = std::make_unique<ui::TrackedElementHandler>(
        web_contents_.get(), context, std::vector<ui::ElementIdentifier>());
    tracked_element_handler_->BindInterface(
        mojo::PendingReceiver<tracked_element::mojom::TrackedElementHandler>());
    handler_ = std::make_unique<TestHelpBubbleHandler>(
        tracked_element_handler_->GetWeakPtr());
    anchor_ = std::make_unique<ui::TrackedElementWebUI>(
        tracked_element_handler_.get(), kTestElementId, kTestElementSecondaryId,
        context);
  }

  void TearDown() override {
    anchor_.reset();
    handler_.reset();
    web_contents_.reset();
    ChromeRenderViewHostTestHarness::TearDown();
  }

 protected:
  std::unique_ptr<content::WebContents> web_contents_;
  std::unique_ptr<ui::TrackedElementHandler> tracked_element_handler_;
  std::unique_ptr<TestHelpBubbleHandler> handler_;
  std::unique_ptr<ui::TrackedElementWebUI> anchor_;
  testing::NiceMock<MockHelpBubbleDelegate> delegate_;
};

TEST_F(BrowserHelpBubbleUnitTest, ForceWebUIHelpBubbles_Logic) {
  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kForcedId1);
  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kForcedId2);
  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kUnforcedId);

  ForceWebUIHelpBubbles::CreateForWebContents(web_contents_.get());
  auto* forced = ForceWebUIHelpBubbles::FromWebContents(web_contents_.get());
  forced->SetForceWebUIForAnchors({kForcedId1, kForcedId2});

  EXPECT_TRUE(forced->ShouldForceWebUIForAnchor(kForcedId1));
  EXPECT_TRUE(forced->ShouldForceWebUIForAnchor(kForcedId2));
  EXPECT_FALSE(forced->ShouldForceWebUIForAnchor(kUnforcedId));
}

TEST_F(BrowserHelpBubbleUnitTest,
       CanBuildBubbleForTrackedElement_MatchingForcedAnchor) {
  FloatingWebUIHelpBubbleFactoryBrowser factory(&delegate_);
  ForceWebUIHelpBubbles::CreateForWebContents(web_contents_.get());
  auto* forced = ForceWebUIHelpBubbles::FromWebContents(web_contents_.get());
  forced->SetForceWebUIForAnchors({kTestElementId});
  EXPECT_FALSE(factory.CanBuildBubbleForTrackedElement(anchor_.get()));
}
