// 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/ash/release_notes/release_notes_notification.h"

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/webui/help_app_ui/help_app_prefs.h"
#include "base/feature_list.h"
#include "base/scoped_observation.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/version.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/prefs/testing_pref_service.h"
#include "components/version_info/version_info.h"
#include "ui/chromeos/devicetype_utils.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/message_center_observer.h"

namespace ash {

class ReleaseNotesNotificationTest
    : public BrowserWithTestWindowTest,
      public ::testing::WithParamInterface</*notification_eligible=*/bool>,
      public message_center::MessageCenterObserver {
 public:
  ReleaseNotesNotificationTest() : notification_eligible_(GetParam()) {}
  ReleaseNotesNotificationTest(const ReleaseNotesNotificationTest&) = delete;
  ReleaseNotesNotificationTest& operator=(const ReleaseNotesNotificationTest&) =
      delete;
  ~ReleaseNotesNotificationTest() override = default;

  // BrowserWithTestWindowTest:
  void SetUp() override {
    BrowserWithTestWindowTest::SetUp();
    message_center_observation_.Observe(message_center::MessageCenter::Get());
    release_notes_notification_ =
        std::make_unique<ReleaseNotesNotification>(profile());
    if (notification_eligible()) {
      scoped_feature_list_.InitWithFeatures(
          /*enabled_features=*/{features::kReleaseNotesNotificationAllChannels,
                                features::
                                    kReleaseNotesNotificationAlwaysEligible},
          /*disabled_features=*/{});
    } else {
      scoped_feature_list_.InitWithFeatures(
          /*enabled_features=*/{features::kReleaseNotesNotificationAllChannels},
          /*disabled_features=*/{
              features::kReleaseNotesNotificationAlwaysEligible});
    }
  }

  void TearDown() override {
    release_notes_notification_.reset();
    message_center_observation_.Reset();
    BrowserWithTestWindowTest::TearDown();
  }

  std::optional<std::string> GetDefaultProfileName() override {
    // TODO(crbug.com/40286020): Use google.com domain to forcibly enable
    // release note notification. Will merge into BrowserWithTestWindowTest.
    return "primary_profile@google.com";
  }

  bool notification_eligible() const { return notification_eligible_; }

 protected:
  bool HasReleaseNotesNotification() {
    return GetReleaseNotesNotification() != nullptr;
  }

  const message_center::Notification* GetReleaseNotesNotification() {
    return message_center::MessageCenter::Get()->FindVisibleNotificationById(
        "show_release_notes_notification");
  }

  base::ScopedObservation<message_center::MessageCenter,
                          message_center::MessageCenterObserver>
      message_center_observation_{this};
  int notification_count_ = 0;
  std::unique_ptr<ReleaseNotesNotification> release_notes_notification_;

 private:
  void OnNotificationAdded(const std::string& notification_id) override {
    if (notification_id == "show_release_notes_notification") {
      ++notification_count_;
    }
  }

  base::test::ScopedFeatureList scoped_feature_list_;
  const bool notification_eligible_;
};

INSTANTIATE_TEST_SUITE_P(All,
                         ReleaseNotesNotificationTest,
                         /*notification_eligible=*/testing::Bool());

TEST_P(ReleaseNotesNotificationTest, DoNotShowReleaseNotesNotification) {
  if (notification_eligible()) {
    GTEST_SKIP() << "Notification is always shown with the notification "
                    "eligible feature turned on.";
  }
  auto release_notes_storage = std::make_unique<ReleaseNotesStorage>(profile());

  // Set the pref to the last shown milestone to ensure release notes do not
  // show.
  profile()->GetPrefs()->SetInteger(
      ash::help_app::prefs::kHelpAppNotificationLastShownMilestone,
      version_info::GetVersion().components()[0]);

  release_notes_notification_->MaybeShowReleaseNotes();
  EXPECT_FALSE(HasReleaseNotesNotification());
  EXPECT_EQ(0, notification_count_);
}

TEST_P(ReleaseNotesNotificationTest, ShowReleaseNotesNotification) {
  auto release_notes_storage = std::make_unique<ReleaseNotesStorage>(profile());

  // Set the pref to an older milestone to ensure release notes do show.
  profile()->GetPrefs()->SetInteger(
      ash::help_app::prefs::kHelpAppNotificationLastShownMilestone, 20);

  release_notes_notification_->MaybeShowReleaseNotes();

  // If notification eligible is enabled, then release notes should show.
  EXPECT_EQ(notification_eligible(), HasReleaseNotesNotification());
  EXPECT_EQ(notification_eligible() ? 1 : 0, notification_count_);
  EXPECT_EQ(notification_eligible(),
            release_notes_storage->ShouldShowSuggestionChip());
  if (HasReleaseNotesNotification()) {
    EXPECT_TRUE(HasReleaseNotesNotification());
    EXPECT_EQ(ui::SubstituteChromeOSDeviceType(
                  IDS_RELEASE_NOTES_DEVICE_SPECIFIC_NOTIFICATION_TITLE),
              GetReleaseNotesNotification()->title());
    EXPECT_EQ("Get highlights from the latest update",
              base::UTF16ToASCII(GetReleaseNotesNotification()->message()));
    // And it show the release notes suggestion chip.
    EXPECT_EQ(3, profile()->GetPrefs()->GetInteger(
                     ash::prefs::kReleaseNotesSuggestionChipTimesLeftToShow));
  }
}

}  // namespace ash
