// 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 <atomic>
#include <optional>

#include "base/metrics/histogram_base.h"
#include "base/metrics/statistics_recorder.h"
#include "base/test/metrics/histogram_tester.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
#include "chrome/browser/ui/browser_element_identifiers.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/read_anything/read_anything_controller.h"
#include "chrome/browser/ui/read_anything/read_anything_entry_point_controller.h"
#include "chrome/browser/ui/read_anything/read_anything_enums.h"
#include "chrome/browser/ui/read_anything/read_anything_side_panel_controller_utils.h"
#include "chrome/browser/ui/side_panel/side_panel_action_callback.h"
#include "chrome/browser/ui/side_panel/side_panel_enums.h"
#include "chrome/browser/ui/side_panel/side_panel_registry.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/views/page_action/test_support/page_action_interactive_test_mixin.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/user_education/interactive_feature_promo_test.h"
#include "components/accessibility/reading/distillable_pages.h"
#include "components/feature_engagement/public/feature_constants.h"
#include "components/tabs/public/tab_interface.h"
#include "components/user_education/views/help_bubble_view.h"
#include "content/public/test/browser_test.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/accessibility/accessibility_features.h"

class ReadAnythingOmniboxControllerTest
    : public PageActionInteractiveTestMixin<InteractiveFeaturePromoTest> {
 public:
  template <typename... Args>
  explicit ReadAnythingOmniboxControllerTest(Args&&... args)
      : PageActionInteractiveTestMixin(UseDefaultTrackerAllowingPromos(
            {feature_engagement::kIPHReadingModePageActionLabelFeature})) {}

  void SetUp() override {
    ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
    features_.InitWithFeatures(
        {features::kReadAnythingOmniboxChip,
         feature_engagement::kIPHReadingModePageActionLabelFeature},
        {});
    distillable_url_ = embedded_test_server()->GetURL("/long_text_page.html");
    non_distillable_url_ = GURL("chrome://blank");
    ReadAnythingController::SetFreezeDistillationOnCreationForTesting(true);
    InteractiveFeaturePromoTest::SetUp();
  }

  void SetUpOnMainThread() override {
    InteractiveFeaturePromoTest::SetUpOnMainThread();
    embedded_test_server()->StartAcceptingConnections();
    OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->GetProfile())
        ->AddHintForTesting(
            distillable_url_, optimization_guide::proto::READER_MODE_ELIGIBLE,
            std::optional<optimization_guide::OptimizationMetadata>());
  }

  void TearDownOnMainThread() override {
    EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
    ReadAnythingController::SetFreezeDistillationOnCreationForTesting(false);
    InteractiveFeaturePromoTest::TearDownOnMainThread();
  }

  using PageActionInteractiveTestMixin::InvokePageAction;
  using PageActionInteractiveTestMixin::WaitForPageActionChipNotVisible;
  using PageActionInteractiveTestMixin::WaitForPageActionChipVisible;
  using PageActionInteractiveTestMixin::WaitForPageActionIconVisible;

  auto WaitForPageActionChipVisible() {
    MultiStep steps;
    steps += WaitForPageActionChipVisible(kActionSidePanelShowReadAnything);
    return steps;
  }

  auto WaitForPageActionIconVisible() {
    MultiStep steps;
    steps += WaitForPageActionIconVisible(kActionSidePanelShowReadAnything);
    return steps;
  }

  auto WaitForPageActionChipNotVisible() {
    MultiStep steps;
    steps += WaitForPageActionChipNotVisible(kActionSidePanelShowReadAnything);
    return steps;
  }

  auto InvokePageAction() {
    MultiStep steps;
    steps += InvokePageAction(kActionSidePanelShowReadAnything);
    return steps;
  }

  auto CloseTab(ui::ElementIdentifier tab) {
    return InAnyContext(WithElement(tab, [this](ui::TrackedElement* el) {
                          content::WebContents* contents =
                              AsInstrumentedWebContents(el)->web_contents();
                          chrome::CloseWebContents(browser(), contents, true);
                        }).SetMustRemainVisible(false));
  }

  // Mimic dwelling on the current page for 4 seconds.
  auto MockDwellTime(ui::ElementIdentifier tab) {
    return InAnyContext(WithElement(tab, [this](ui::TrackedElement* el) {
      content::WebContents* contents =
          InteractiveFeaturePromoTest::AsInstrumentedWebContents(el)
              ->web_contents();
      tabs::TabInterface* tab_interface =
          browser()->GetTabStripModel()->GetTabForWebContents(contents);
      CHECK(tab_interface);
      auto* read_anything_controller =
          ReadAnythingController::From(tab_interface);
      CHECK(read_anything_controller);
      read_anything_controller->SetDwellTimeForTesting(base::TimeTicks::Now() -
                                                       base::Seconds(4));
    }));
  }

  base::HistogramTester& histogram_tester() { return histogram_tester_; }

  GURL distillable_url_;
  GURL non_distillable_url_;
  base::test::ScopedFeatureList features_;
  base::HistogramTester histogram_tester_;
};

IN_PROC_BROWSER_TEST_F(ReadAnythingOmniboxControllerTest,
                       ShowAndHideOmniboxAfterTabSwitch) {
  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kFirstTab);
  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kSecondTab);
  RunTestSequence(
      // First tab is non-distillable, second tab is distillable.
      InstrumentTab(kFirstTab),
      NavigateWebContents(kFirstTab, non_distillable_url_),
      AddInstrumentedTab(kSecondTab, distillable_url_),

      // Select the second tab, wait for chip to show.
      SelectTab(kTabStripElementId, 1), WaitForPageActionChipVisible(),

      // Select the first tab, wait for chip to hide.
      SelectTab(kTabStripElementId, 0), WaitForPageActionChipNotVisible());
}

IN_PROC_BROWSER_TEST_F(ReadAnythingOmniboxControllerTest,
                       ShowOmniboxChipImmediatelyAfterReadingModeClosed) {
  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab);
  RunTestSequence(
      InstrumentTab(kActiveTab),
      NavigateWebContents(kActiveTab, distillable_url_),
      WaitForPageActionChipVisible(), InvokePageAction(),
      WaitForPageActionChipNotVisible(),
      // Close RM so the omnibox chip can show again.
      Do([&]() {
        auto context = actions::ActionInvocationContext();
        context.SetProperty(
            kSidePanelOpenTriggerKey,
            static_cast<int>(SidePanelOpenTrigger::kPinnedEntryToolbarButton));
        read_anything::ReadAnythingEntryPointController::InvokePageAction(
            browser(), context);
      }),
      WaitForPageActionChipVisible());
}

IN_PROC_BROWSER_TEST_F(ReadAnythingOmniboxControllerTest,
                       ShowAndHideOmniboxAfterNavigation) {
  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab);
  RunTestSequence(
      InstrumentTab(kActiveTab),
      NavigateWebContents(kActiveTab, distillable_url_),
      WaitForWebContentsReady(kActiveTab), WaitForPageActionChipVisible(),
      NavigateWebContents(kActiveTab, non_distillable_url_),
      WaitForWebContentsReady(kActiveTab), WaitForPageActionChipNotVisible());
}

IN_PROC_BROWSER_TEST_F(ReadAnythingOmniboxControllerTest,
                       HideOmniboxAfterEntryShown) {
  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab);
  RunTestSequence(InstrumentTab(kActiveTab),
                  NavigateWebContents(kActiveTab, distillable_url_),
                  WaitForWebContentsReady(kActiveTab),
                  WaitForPageActionChipVisible(), InvokePageAction(),
                  WaitForPageActionChipNotVisible());
}

IN_PROC_BROWSER_TEST_F(ReadAnythingOmniboxControllerTest,
                       ShowAndHideIphAfterTabSwitch) {
  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kFirstTab);
  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kSecondTab);
  RunTestSequence(
      // First tab is non-distillable, second tab is distillable.
      InstrumentTab(kFirstTab),
      NavigateWebContents(kFirstTab, non_distillable_url_),
      AddInstrumentedTab(kSecondTab, distillable_url_),

      // Select the second tab, wait for promo to show.
      SelectTab(kTabStripElementId, 1),
      WaitForPromo(feature_engagement::kIPHReadingModePageActionLabelFeature),

      // Select the first tab, wait for promo to hide.
      SelectTab(kTabStripElementId, 0),
      WaitForHide(
          user_education::HelpBubbleView::kHelpBubbleElementIdForTesting));
}

IN_PROC_BROWSER_TEST_F(ReadAnythingOmniboxControllerTest,
                       ShowAndHideIphAfterNavigation) {
  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab);
  RunTestSequence(
      InstrumentTab(kActiveTab),
      NavigateWebContents(kActiveTab, distillable_url_),
      // Show the Iph after navigating to a distillable domain.
      WaitForPromo(feature_engagement::kIPHReadingModePageActionLabelFeature),
      // Hide the Iph after navigating to a non-distillable domain.
      NavigateWebContents(kActiveTab, non_distillable_url_),
      WaitForWebContentsReady(kActiveTab),
      WaitForHide(
          user_education::HelpBubbleView::kHelpBubbleElementIdForTesting));
}

IN_PROC_BROWSER_TEST_F(ReadAnythingOmniboxControllerTest,
                       HideIPHAfterEntryShown) {
  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab);
  RunTestSequence(
      InstrumentTab(kActiveTab),
      NavigateWebContents(kActiveTab, distillable_url_),
      WaitForWebContentsReady(kActiveTab), WaitForPageActionChipVisible(),
      WaitForPromo(feature_engagement::kIPHReadingModePageActionLabelFeature),
      InvokePageAction(),
      WaitForHide(
          user_education::HelpBubbleView::kHelpBubbleElementIdForTesting));
}
