// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <memory>
#include <string>
#include <vector>

#include "ash/constants/ash_pref_names.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/system/mahi/mahi_constants.h"
#include "ash/system/mahi/mahi_panel_view.h"
#include "ash/system/mahi/mahi_ui_controller.h"
#include "ash/system/mahi/mahi_utils.h"
#include "ash/system/mahi/test/mock_mahi_manager.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/ash_test_helper.h"
#include "ash/test/ash_test_util.h"
#include "ash/test/pixel/ash_pixel_differ.h"
#include "ash/test/pixel/ash_pixel_test_helper.h"
#include "ash/test/pixel/ash_pixel_test_init_params.h"
#include "base/strings/strcat.h"
#include "base/test/scoped_feature_list.h"
#include "chromeos/components/mahi/public/cpp/mahi_manager.h"
#include "chromeos/constants/chromeos_features.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/image/image_unittest_util.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/test/views_test_utils.h"
#include "ui/views/view_utils.h"
#include "ui/views/widget/widget.h"

namespace ash {

// Pixel tests for Chrome OS Status Area. This relates to all tray buttons in
// the bottom right corner.
class MahiPanelViewPixelTest
    : public AshTestBase,
      public testing::WithParamInterface</*enable_system_blur=*/bool> {
 public:
  // AshTestBase:
  std::optional<pixel_test::InitParams> CreatePixelTestInitParams()
      const override {
    pixel_test::InitParams init_params;
    init_params.system_blur_enabled = GetParam();
    return init_params;
  }

  // AshTestBase:
  void SetUp() override {
    scoped_feature_list_.InitWithFeatures(
        {chromeos::features::kFeatureManagementMahi}, {});
    AshTestBase::SetUp();

    scoped_setter_ = std::make_unique<chromeos::ScopedMahiManagerSetter>(
        &mock_mahi_manager_);

    CreatePanelWidget();
  }

  void TearDown() override {
    panel_view_ = nullptr;
    widget_.reset();
    scoped_setter_.reset();

    AshTestBase::TearDown();
  }

  void CreatePanelWidget() {
    widget_ = CreateFramelessTestWidget();
    widget_->SetBounds(
        gfx::Rect(/*x=*/0, /*y=*/0,
                  /*width=*/mahi_constants::kPanelDefaultWidth,
                  /*height=*/mahi_constants::kPanelDefaultHeight));
    panel_view_ = widget_->SetContentsView(
        std::make_unique<MahiPanelView>(&ui_controller_));
  }

  void RecreatePanelWidget() {
    panel_view_ = nullptr;
    widget_.reset();

    CreatePanelWidget();
  }

  // Scroll the scroll view inside Mahi panel to the bottom.
  void ScrollToBottom() {
    auto* scroll_view = views::AsViewClass<views::ScrollView>(
        panel_view()->GetViewByID(mahi_constants::ViewId::kScrollView));
    ASSERT_TRUE(scroll_view);
    scroll_view->vertical_scroll_bar()->ScrollByAmount(
        views::ScrollBar::ScrollAmount::kEnd);
  }

  MockMahiManager& mock_mahi_manager() { return mock_mahi_manager_; }

  MahiUiController* ui_controller() { return &ui_controller_; }

  MahiPanelView* panel_view() { return panel_view_; }

  views::Widget* widget() { return widget_.get(); }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
  testing::NiceMock<MockMahiManager> mock_mahi_manager_;
  std::unique_ptr<chromeos::ScopedMahiManagerSetter> scoped_setter_;
  MahiUiController ui_controller_;
  raw_ptr<MahiPanelView> panel_view_ = nullptr;
  std::unique_ptr<views::Widget> widget_;
};

INSTANTIATE_TEST_SUITE_P(
    /* no prefix */,
    MahiPanelViewPixelTest,
    testing::Bool());

TEST_P(MahiPanelViewPixelTest, MainPanel) {
  ON_CALL(mock_mahi_manager(), GetContentTitle)
      .WillByDefault(testing::Return(u"Test content title"));
  ON_CALL(mock_mahi_manager(), GetContentIcon)
      .WillByDefault(testing::Return(
          gfx::test::CreateImageSkia(/*size=*/128, SK_ColorBLUE)));

  ON_CALL(mock_mahi_manager(), GetSummary)
      .WillByDefault([](chromeos::MahiManager::MahiSummaryCallback callback) {
        std::move(callback).Run(
            base::StrCat(std::vector<std::u16string>(30, u"Summary text ")),
            chromeos::MahiResponseStatus::kSuccess);
      });

  ui_controller()->RefreshContents();
  views::test::RunScheduledLayout(widget());

  EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
      GenerateScreenshotName("panel_view"),
      /*revision_number=*/pixel_test_helper()->IsSystemBlurEnabled() ? 15 : 0,
      panel_view()));
}

TEST_P(MahiPanelViewPixelTest, ContentSourceButton) {
  ON_CALL(mock_mahi_manager(), GetContentTitle)
      .WillByDefault(testing::Return(base::StrCat(
          std::vector<std::u16string>(3, u"Long content title "))));
  ON_CALL(mock_mahi_manager(), GetContentIcon)
      .WillByDefault(testing::Return(
          gfx::test::CreateImageSkia(/*size=*/200, SK_ColorRED)));

  ui_controller()->RefreshContents();
  views::test::RunScheduledLayout(widget());

  EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
      GenerateScreenshotName("content_source"),
      /*revision_number=*/pixel_test_helper()->IsSystemBlurEnabled() ? 3 : 0,
      panel_view()->GetViewByID(mahi_constants::ViewId::kContentSourceButton)));
}

TEST_P(MahiPanelViewPixelTest, SummaryView) {
  ON_CALL(mock_mahi_manager(), GetSummary)
      .WillByDefault([](chromeos::MahiManager::MahiSummaryCallback callback) {
        std::move(callback).Run(
            base::StrCat(std::vector<std::u16string>(35, u"Summary text ")),
            chromeos::MahiResponseStatus::kSuccess);
      });

  ui_controller()->RefreshContents();
  views::test::RunScheduledLayout(widget());

  EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
      GenerateScreenshotName("summary_view"),
      /*revision_number=*/pixel_test_helper()->IsSystemBlurEnabled() ? 8 : 0,
      panel_view()->GetViewByID(mahi_constants::ViewId::kScrollView)));
}

TEST_P(MahiPanelViewPixelTest, PanelWithoutFeedbackButtons) {
  Shell::Get()->session_controller()->GetActivePrefService()->SetInteger(
      prefs::kHmrManagedSettings,
      static_cast<int>(
          mahi_utils::HmrEnterprisePolicy::kAllowedWithoutModelImprovement));
  ON_CALL(mock_mahi_manager(), GetSummary)
      .WillByDefault([](chromeos::MahiManager::MahiSummaryCallback callback) {
        std::move(callback).Run(
            base::StrCat(std::vector<std::u16string>(35, u"Summary text ")),
            chromeos::MahiResponseStatus::kSuccess);
      });

  RecreatePanelWidget();

  EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
      GenerateScreenshotName("scroll_view"),
      /*revision_number=*/pixel_test_helper()->IsSystemBlurEnabled() ? 3 : 0,
      panel_view()->GetViewByID(mahi_constants::ViewId::kScrollView)));

  EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
      GenerateScreenshotName("footer"),
      /*revision_number=*/pixel_test_helper()->IsSystemBlurEnabled() ? 1 : 0,
      panel_view()->GetViewByID(mahi_constants::ViewId::kFooterLabel)));
}

TEST_P(MahiPanelViewPixelTest, QuestionAnswerViewBasic) {
  auto* const send_button =
      panel_view()->GetViewByID(mahi_constants::ViewId::kAskQuestionSendButton);
  auto* const question_textfield = views::AsViewClass<views::Textfield>(
      panel_view()->GetViewByID(mahi_constants::ViewId::kQuestionTextfield));

  const std::u16string answer(u"test answer");
  ON_CALL(mock_mahi_manager(), AnswerQuestion)
      .WillByDefault(
          [&answer](
              const std::u16string& question, bool current_panel_content,
              chromeos::MahiManager::MahiAnswerQuestionCallback callback) {
            std::move(callback).Run(answer,
                                    chromeos::MahiResponseStatus::kSuccess);
          });

  // Set a valid text in the question textfield.
  const std::u16string question(u"question");
  question_textfield->SetText(question);
  send_button->SetEnabled(true);

  // Pressing the send button should create a question and answer text bubble.
  LeftClickOn(send_button);

  views::test::RunScheduledLayout(widget());

  EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
      GenerateScreenshotName("question_answer_view_basic"),
      /*revision_number=*/pixel_test_helper()->IsSystemBlurEnabled() ? 9 : 0,
      panel_view()->GetViewByID(mahi_constants::ViewId::kScrollView)));
}

TEST_P(MahiPanelViewPixelTest, QuestionAnswerViewLongText) {
  auto* const send_button =
      panel_view()->GetViewByID(mahi_constants::ViewId::kAskQuestionSendButton);
  auto* const question_textfield = views::AsViewClass<views::Textfield>(
      panel_view()->GetViewByID(mahi_constants::ViewId::kQuestionTextfield));

  const std::u16string answer =
      base::StrCat(std::vector<std::u16string>(25, u"Long Answer "));
  ON_CALL(mock_mahi_manager(), AnswerQuestion)
      .WillByDefault(
          [&answer](
              const std::u16string& question, bool current_panel_content,
              chromeos::MahiManager::MahiAnswerQuestionCallback callback) {
            std::move(callback).Run(answer,
                                    chromeos::MahiResponseStatus::kSuccess);
          });

  // Set a valid text in the question textfield.
  const std::u16string question =
      base::StrCat(std::vector<std::u16string>(25, u"Long Question "));
  question_textfield->SetText(question);
  send_button->SetEnabled(true);

  // Pressing the send button should create a question and answer text bubble.
  LeftClickOn(send_button);

  views::test::RunScheduledLayout(widget());

  EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
      GenerateScreenshotName("question_answer_view_long_text"),
      /*revision_number=*/pixel_test_helper()->IsSystemBlurEnabled() ? 11 : 0,
      panel_view()->GetViewByID(mahi_constants::ViewId::kScrollView)));
}

TEST_P(MahiPanelViewPixelTest, SummaryViewScrollToBottom) {
  ON_CALL(mock_mahi_manager(), GetSummary)
      .WillByDefault([](chromeos::MahiManager::MahiSummaryCallback callback) {
        std::move(callback).Run(
            base::StrCat(std::vector<std::u16string>(60, u"Summary text ")),
            chromeos::MahiResponseStatus::kSuccess);
      });

  ui_controller()->RefreshContents();
  views::test::RunScheduledLayout(widget());

  ScrollToBottom();

  EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
      GenerateScreenshotName("summary_view_bottom"),
      /*revision_number=*/pixel_test_helper()->IsSystemBlurEnabled() ? 7 : 0,
      panel_view()->GetViewByID(mahi_constants::ViewId::kScrollView)));
}

TEST_P(MahiPanelViewPixelTest, QuestionAnswerViewScrollToBottom) {
  const std::u16string answer =
      base::StrCat(std::vector<std::u16string>(35, u"Long Answer "));
  ON_CALL(mock_mahi_manager(), AnswerQuestion)
      .WillByDefault(
          [&answer](
              const std::u16string& question, bool current_panel_content,
              chromeos::MahiManager::MahiAnswerQuestionCallback callback) {
            std::move(callback).Run(answer,
                                    chromeos::MahiResponseStatus::kSuccess);
          });

  // Set a valid text in the question textfield.
  const std::u16string question =
      base::StrCat(std::vector<std::u16string>(25, u"Long Question "));
  views::AsViewClass<views::Textfield>(
      panel_view()->GetViewByID(mahi_constants::ViewId::kQuestionTextfield))
      ->SetText(question);

  // Pressing the send button should create a question and answer text bubble.
  auto* const send_button =
      panel_view()->GetViewByID(mahi_constants::ViewId::kAskQuestionSendButton);
  send_button->SetEnabled(true);
  LeftClickOn(send_button);

  views::test::RunScheduledLayout(widget());

  ScrollToBottom();

  EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
      GenerateScreenshotName("question_answer_bottom"),
      /*revision_number=*/pixel_test_helper()->IsSystemBlurEnabled() ? 8 : 0,
      panel_view()->GetViewByID(mahi_constants::ViewId::kScrollView)));
}

}  // namespace ash
