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

#include "ash/system/unified/quick_settings_header.h"

#include <memory>

#include "ash/login/ui/fake_login_detachable_base_model.h"
#include "ash/login/ui/lock_contents_view_test_api.h"
#include "ash/login/ui/lock_screen.h"
#include "ash/login/ui/login_test_base.h"
#include "ash/public/cpp/ash_view_ids.h"
#include "ash/public/cpp/test/test_system_tray_client.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/system/extended_updates/extended_updates_metrics.h"
#include "ash/system/model/enterprise_domain_model.h"
#include "ash/system/model/system_tray_model.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "ash/system/unified/unified_system_tray_model.h"
#include "ash/system/update/eol_notice_quick_settings_view.h"
#include "ash/test/ash_test_base.h"
#include "ash/test_shell_delegate.h"
#include "base/check.h"
#include "base/feature_list.h"
#include "base/memory/raw_ptr.h"
#include "base/test/metrics/histogram_tester.h"
#include "chromeos/ash/components/login/auth/auth_events_recorder.h"
#include "components/user_manager/user_type.h"
#include "components/version_info/channel.h"
#include "ui/events/test/event_generator.h"
#include "ui/views/controls/label.h"
#include "ui/views/view_utils.h"

namespace ash {
namespace {

EnterpriseDomainModel* GetEnterpriseDomainModel() {
  return Shell::Get()->system_tray_model()->enterprise_domain();
}

}  // namespace

class QuickSettingsHeaderTest : public NoSessionAshTestBase {
 public:
  QuickSettingsHeaderTest() = default;

  // AshTestBase:
  void SetUp() override {
    // Install a test delegate to allow overriding channel version.
    auto delegate = std::make_unique<TestShellDelegate>();
    test_shell_delegate_ = delegate.get();
    set_shell_delegate(std::move(delegate));
    NoSessionAshTestBase::SetUp();

    model_ = base::MakeRefCounted<UnifiedSystemTrayModel>(nullptr);
    controller_ = std::make_unique<UnifiedSystemTrayController>(model_.get());
  }

  void TearDown() override {
    header_ = nullptr;
    widget_.reset();
    controller_.reset();
    model_.reset();
    test_shell_delegate_ = nullptr;
    NoSessionAshTestBase::TearDown();
  }

  // Creates the object under test. Not part of SetUp() because sometimes tests
  // need to setup the shell delegate or login before creating the header.
  void CreateQuickSettingsHeader() {
    // Instantiate view.
    auto header = std::make_unique<QuickSettingsHeader>(controller_.get());
    header_ = header.get();

    // Place the view in a large views::Widget so the buttons are clickable.
    widget_ = CreateFramelessTestWidget();
    widget_->SetFullscreen(true);
    widget_->SetContentsView(std::move(header));
  }

  views::View* GetManagedButton() {
    return header_->GetViewByID(VIEW_ID_QS_MANAGED_BUTTON);
  }

  views::Label* GetManagedButtonLabel() {
    return header_->GetManagedButtonLabelForTest();
  }

  views::View* GetSupervisedButton() {
    return header_->GetViewByID(VIEW_ID_QS_SUPERVISED_BUTTON);
  }

  views::Label* GetSupervisedButtonLabel() {
    return header_->GetSupervisedButtonLabelForTest();
  }

  raw_ptr<TestShellDelegate> test_shell_delegate_ = nullptr;
  scoped_refptr<UnifiedSystemTrayModel> model_;
  std::unique_ptr<UnifiedSystemTrayController> controller_;
  std::unique_ptr<views::Widget> widget_;
  raw_ptr<QuickSettingsHeader> header_ = nullptr;
};

TEST_F(QuickSettingsHeaderTest, HiddenOnStable) {
  test_shell_delegate_->set_channel(version_info::Channel::STABLE);

  CreateQuickSettingsHeader();

  EXPECT_FALSE(GetManagedButton()->GetVisible());
  EXPECT_FALSE(GetSupervisedButton()->GetVisible());

  // Channel view is not created.
  EXPECT_FALSE(header_->channel_view_for_test());

  // Since no views are created, the header is hidden.
  EXPECT_FALSE(header_->GetVisible());
}

TEST_F(QuickSettingsHeaderTest, ShowChannelViewBeforeLoginOnNonStable) {
  test_shell_delegate_->set_channel(version_info::Channel::BETA);

  CreateQuickSettingsHeader();

  EXPECT_TRUE(header_->channel_view_for_test());
  EXPECT_TRUE(header_->GetVisible());
}

TEST_F(QuickSettingsHeaderTest, ShowsChannelViewAfterLogin) {
  test_shell_delegate_->set_channel(version_info::Channel::BETA);
  SimulateUserLogin({"user@gmail.com"});

  CreateQuickSettingsHeader();

  // Channel view is created.
  EXPECT_TRUE(header_->channel_view_for_test());

  // Header is shown.
  EXPECT_TRUE(header_->GetVisible());
}

TEST_F(QuickSettingsHeaderTest, EolNoticeVisible) {
  Shell::Get()->system_tray_model()->SetShowEolNotice(true);
  SimulateUserLogin({"user@gmail.com"});

  CreateQuickSettingsHeader();
  // Header is shown.
  EXPECT_TRUE(header_->GetVisible());

  // EOL notice is visible.
  auto* eol_notice_view = header_->eol_notice_for_test();
  ASSERT_TRUE(eol_notice_view);
  EXPECT_TRUE(eol_notice_view->GetVisible());

  LeftClickOn(eol_notice_view);
  EXPECT_EQ(1, GetSystemTrayClient()->show_eol_info_count());
}

TEST_F(QuickSettingsHeaderTest, EolNoticeNotVisibleBeforeLogin) {
  test_shell_delegate_->set_channel(version_info::Channel::BETA);
  Shell::Get()->system_tray_model()->SetShowEolNotice(true);
  CreateQuickSettingsHeader();

  // Header is shown.
  EXPECT_TRUE(header_->GetVisible());

  // Channel view is created.
  EXPECT_TRUE(header_->channel_view_for_test());

  // EOL notice is not visible.
  EXPECT_FALSE(header_->eol_notice_for_test());
}

TEST_F(QuickSettingsHeaderTest, ChannelIndicatorNotShownWithEolNotice) {
  test_shell_delegate_->set_channel(version_info::Channel::BETA);
  SimulateUserLogin({"user@gmail.com"});

  Shell::Get()->system_tray_model()->SetShowEolNotice(true);

  CreateQuickSettingsHeader();
  // Header is shown.
  EXPECT_TRUE(header_->GetVisible());

  // No channel indicator.
  EXPECT_FALSE(header_->channel_view_for_test());

  // EOL notice is visible.
  ASSERT_TRUE(header_->eol_notice_for_test());
  EXPECT_TRUE(header_->eol_notice_for_test()->GetVisible());
}

TEST_F(QuickSettingsHeaderTest, ExtendedUpdatesNoticeVisible) {
  Shell::Get()->system_tray_model()->SetShowExtendedUpdatesNotice(true);
  SimulateUserLogin({"user@gmail.com"});

  base::HistogramTester histogram_tester;
  CreateQuickSettingsHeader();

  // Header is shown.
  EXPECT_TRUE(header_->GetVisible());
  histogram_tester.ExpectBucketCount(
      kExtendedUpdatesEntryPointEventMetric,
      ExtendedUpdatesEntryPointEvent::kQuickSettingsBannerShown, 1);

  // Extended Updates notice is visible.
  auto* extended_updates_notice_view = header_->GetExtendedUpdatesViewForTest();
  ASSERT_TRUE(extended_updates_notice_view);
  EXPECT_TRUE(extended_updates_notice_view->GetVisible());

  EXPECT_EQ(0, GetSystemTrayClient()->show_about_chromeos_count());
  LeftClickOn(extended_updates_notice_view);
  EXPECT_EQ(1, GetSystemTrayClient()->show_about_chromeos_count());
  histogram_tester.ExpectBucketCount(
      kExtendedUpdatesEntryPointEventMetric,
      ExtendedUpdatesEntryPointEvent::kQuickSettingsBannerClicked, 1);
}

TEST_F(QuickSettingsHeaderTest, ExtendedUpdatesNoticeNotVisibleBeforeLogin) {
  test_shell_delegate_->set_channel(version_info::Channel::BETA);
  Shell::Get()->system_tray_model()->SetShowExtendedUpdatesNotice(true);
  CreateQuickSettingsHeader();

  // Header is shown.
  EXPECT_TRUE(header_->GetVisible());

  // Channel view is created.
  EXPECT_TRUE(header_->channel_view_for_test());

  // Extended Updates notice is not visible.
  EXPECT_FALSE(header_->GetExtendedUpdatesViewForTest());
}

TEST_F(QuickSettingsHeaderTest,
       ChannelIndicatorNotShownWithExtendedUpdatesNotice) {
  test_shell_delegate_->set_channel(version_info::Channel::BETA);
  SimulateUserLogin({"user@gmail.com"});

  Shell::Get()->system_tray_model()->SetShowExtendedUpdatesNotice(true);

  base::HistogramTester histogram_tester;
  CreateQuickSettingsHeader();

  // Header is shown.
  EXPECT_TRUE(header_->GetVisible());

  // No channel indicator.
  EXPECT_FALSE(header_->channel_view_for_test());

  // Extended Updates notice is visible.
  ASSERT_TRUE(header_->GetExtendedUpdatesViewForTest());
  EXPECT_TRUE(header_->GetExtendedUpdatesViewForTest()->GetVisible());
  histogram_tester.ExpectBucketCount(
      kExtendedUpdatesEntryPointEventMetric,
      ExtendedUpdatesEntryPointEvent::kQuickSettingsBannerShown, 1);
}

TEST_F(QuickSettingsHeaderTest, ExtendedUpdatesNoticeNotShownWithEolNotice) {
  SimulateUserLogin({"user@gmail.com"});

  Shell::Get()->system_tray_model()->SetShowEolNotice(true);
  Shell::Get()->system_tray_model()->SetShowExtendedUpdatesNotice(true);

  base::HistogramTester histogram_tester;
  CreateQuickSettingsHeader();

  // Header is shown.
  EXPECT_TRUE(header_->GetVisible());

  // No channel indicator.
  EXPECT_FALSE(header_->channel_view_for_test());

  // EOL notice is visible.
  ASSERT_TRUE(header_->eol_notice_for_test());
  EXPECT_TRUE(header_->eol_notice_for_test()->GetVisible());

  // Extended Updates notice is not visible.
  EXPECT_FALSE(header_->GetExtendedUpdatesViewForTest());
  histogram_tester.ExpectBucketCount(
      kExtendedUpdatesEntryPointEventMetric,
      ExtendedUpdatesEntryPointEvent::kQuickSettingsBannerShown, 0);
}

TEST_F(QuickSettingsHeaderTest, EnterpriseManagedDeviceVisible) {
  CreateQuickSettingsHeader();

  // Simulate enterprise information becoming available.
  GetEnterpriseDomainModel()->SetDeviceEnterpriseInfo(DeviceEnterpriseInfo{
      "example.com", ManagementDeviceMode::kChromeEnterprise});

  EXPECT_TRUE(GetManagedButton()->GetVisible());
  EXPECT_EQ(GetManagedButtonLabel()->GetText(), u"Managed by example.com");
  EXPECT_EQ(GetManagedButton()->GetRenderedTooltipText({}),
            u"Managed by example.com");
  EXPECT_TRUE(header_->GetVisible());
}

TEST_F(QuickSettingsHeaderTest, EnterpriseManagedAccountVisible) {
  CreateQuickSettingsHeader();

  // Simulate enterprise information becoming available.
  GetEnterpriseDomainModel()->SetEnterpriseAccountDomainInfo("example.com");

  EXPECT_TRUE(GetManagedButton()->GetVisible());
  EXPECT_EQ(GetManagedButtonLabel()->GetText(), u"Managed by example.com");
  EXPECT_EQ(GetManagedButton()->GetRenderedTooltipText({}),
            u"Managed by example.com");
  EXPECT_TRUE(header_->GetVisible());
}

TEST_F(QuickSettingsHeaderTest, BothChannelAndEnterpriseVisible) {
  test_shell_delegate_->set_channel(version_info::Channel::BETA);
  GetEnterpriseDomainModel()->SetDeviceEnterpriseInfo(DeviceEnterpriseInfo{
      "example.com", ManagementDeviceMode::kChromeEnterprise});
  SimulateUserLogin({"user@gmail.com"});

  CreateQuickSettingsHeader();

  EXPECT_TRUE(GetManagedButton()->GetVisible());
  // The label is the shorter "Managed" due to the two-column layout.
  EXPECT_EQ(GetManagedButtonLabel()->GetText(), u"Managed");
  EXPECT_EQ(GetManagedButton()->GetRenderedTooltipText({}),
            u"Managed by example.com");
  EXPECT_TRUE(header_->channel_view_for_test());
  EXPECT_TRUE(header_->GetVisible());
}

TEST_F(QuickSettingsHeaderTest, BothEolNoticeAndEnterpriseVisible) {
  GetEnterpriseDomainModel()->SetDeviceEnterpriseInfo(DeviceEnterpriseInfo{
      "example.com", ManagementDeviceMode::kChromeEnterprise});
  Shell::Get()->system_tray_model()->SetShowEolNotice(true);
  SimulateUserLogin({"user@gmail.com"});

  CreateQuickSettingsHeader();

  EXPECT_TRUE(GetManagedButton()->GetVisible());
  // The label is the shorter "Managed" due to the two-column layout.
  EXPECT_EQ(GetManagedButtonLabel()->GetText(), u"Managed");
  EXPECT_EQ(GetManagedButton()->GetRenderedTooltipText({}),
            u"Managed by example.com");
  EXPECT_TRUE(header_->GetVisible());
  EolNoticeQuickSettingsView* eol_notice = header_->eol_notice_for_test();
  ASSERT_TRUE(eol_notice);
  EXPECT_TRUE(eol_notice->GetVisible());
  // The label is shorter due to the two-column layout.
  EXPECT_EQ(eol_notice->GetText(), u"Updates ended");

  LeftClickOn(eol_notice);
  EXPECT_EQ(1, GetSystemTrayClient()->show_eol_info_count());
}

TEST_F(QuickSettingsHeaderTest, ChildVisible) {
  CreateQuickSettingsHeader();

  // Before login the supervised user view is invisible.
  EXPECT_FALSE(GetSupervisedButton()->GetVisible());

  // Simulate supervised user logging in.
  SessionControllerImpl* session = Shell::Get()->session_controller();
  ClearLogin();
  SimulateUserLogin({"child@test.com", user_manager::UserType::kChild});
  UserSession user_session = *session->GetUserSession(0);
  user_session.custodian_email = "parent@test.com";
  session->UpdateUserSession(std::move(user_session));

  // Recreate the header after login.
  CreateQuickSettingsHeader();

  // Now the supervised user view is visible.
  EXPECT_TRUE(GetSupervisedButton()->GetVisible());
  EXPECT_EQ(GetSupervisedButtonLabel()->GetText(), u"Supervised user");
  EXPECT_EQ(GetSupervisedButton()->GetRenderedTooltipText({}),
            u"Account managed by parent@test.com");
  EXPECT_TRUE(header_->GetVisible());

  LeftClickOn(GetSupervisedButton());
  EXPECT_EQ(GetSystemTrayClient()->show_account_settings_count(), 1);
}

TEST_F(QuickSettingsHeaderTest, ShowManagementDisclosure) {
  // This test relies on the lock screen actually being created (and creating
  // the lock screen requires the existence of an `AuthEventsRecorder`).
  std::unique_ptr<AuthEventsRecorder> auth_events_recorder =
      AuthEventsRecorder::CreateForTesting();

  // Setup to lock screen.
  ClearLogin();
  TestSessionControllerClient* client = GetSessionControllerClient();
  client->set_show_lock_screen_views(true);
  client->LockScreen();
  client->SetSessionState(session_manager::SessionState::LOCKED);

  // Create new lock screen for lock_contents.
  ash::LockScreen::Get()->Destroy();
  LockScreen::Show(LockScreen::ScreenType::kLock);

  QuickSettingsHeader::ShowEnterpriseInfo(controller_.get(), true, true, true);
  LockContentsViewTestApi lock_contents(
      LockScreen::TestApi(LockScreen::Get()).contents_view());

  EXPECT_TRUE(lock_contents.management_disclosure_dialog());
}

TEST_F(QuickSettingsHeaderTest, DoNotShowManagementDisclosure) {
  // This test relies on the lock screen actually being created (and creating
  // the lock screen requires the existence of an `AuthEventsRecorder`).
  std::unique_ptr<AuthEventsRecorder> auth_events_recorder =
      AuthEventsRecorder::CreateForTesting();

  // Setup to lock screen.
  ClearLogin();
  TestSessionControllerClient* client = GetSessionControllerClient();
  client->set_show_lock_screen_views(true);
  client->LockScreen();
  client->SetSessionState(session_manager::SessionState::LOCKED);

  // Create new lock screen for lock_contents.
  ash::LockScreen::Get()->Destroy();
  LockScreen::Show(LockScreen::ScreenType::kLock);

  // Feature disabled.
  QuickSettingsHeader::ShowEnterpriseInfo(
      controller_.get(), /*showManagementDisclosureDialog*/ false,
      /*IsUserSessionBlocked*/ true, /*hasEnterpriseDomainManager*/ true);
  LockContentsViewTestApi lock_contents_1(
      LockScreen::TestApi(LockScreen::Get()).contents_view());
  EXPECT_FALSE(lock_contents_1.management_disclosure_dialog());

  // User session not blocked.
  QuickSettingsHeader::ShowEnterpriseInfo(
      controller_.get(), /*showManagementDisclosureDialog*/ true,
      /*IsUserSessionBlocked*/ false, /*hasEnterpriseDomainManager*/ true);
  LockContentsViewTestApi lock_contents_2(
      LockScreen::TestApi(LockScreen::Get()).contents_view());
  EXPECT_FALSE(lock_contents_2.management_disclosure_dialog());

  // Not enterprise enrolled device.
  QuickSettingsHeader::ShowEnterpriseInfo(
      controller_.get(), /*showManagementDisclosureDialog*/ true,
      /*IsUserSessionBlocked*/ true, /*hasEnterpriseDomainManager*/ false);
  LockContentsViewTestApi lock_contents_3(
      LockScreen::TestApi(LockScreen::Get()).contents_view());
  EXPECT_FALSE(lock_contents_3.management_disclosure_dialog());
}

}  // namespace ash
