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

#include <optional>

#include "base/functional/callback.h"
#include "base/strings/strcat.h"
#include "base/task/bind_post_task.h"
#include "base/task/thread_pool.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/enterprise/client_certificates/certificate_provisioning_service_factory.h"
#include "chrome/browser/enterprise/identifiers/profile_id_service_factory.h"
#include "chrome/browser/enterprise/reporting/test/test_utils.h"
#include "chrome/browser/enterprise/test/management_context_mixin.h"
#include "chrome/browser/net/profile_network_context_service.h"
#include "chrome/browser/net/profile_network_context_service_factory.h"
#include "chrome/browser/policy/chrome_browser_policy_connector.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/test/base/chrome_test_utils.h"
#include "chrome/test/base/mixin_based_in_process_browser_test.h"
#include "chrome/test/base/platform_browser_test.h"
#include "components/device_signals/core/browser/browser_utils.h"
#include "components/device_signals/core/common/platform_utils.h"
#include "components/device_signals/core/common/signals_features.h"
#include "components/enterprise/browser/controller/chrome_browser_cloud_management_controller.h"
#include "components/enterprise/browser/identifiers/profile_id_service.h"
#include "components/enterprise/browser/reporting/report_scheduler.h"
#include "components/enterprise/browser/reporting/report_uploader.h"
#include "components/enterprise/browser/reporting/report_util.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/policy/core/browser/url_list/policy_blocklist_service.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/cloud/cloud_policy_util.h"
#include "components/policy/core/common/policy_switches.h"
#include "components/policy/policy_constants.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "components/version_info/version_info.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "crypto/keypair.h"
#include "google_apis/gaia/gaia_constants.h"
#include "net/base/url_util.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
#include "net/dns/mock_host_resolver.h"
#include "net/ssl/client_cert_identity.h"
#include "net/ssl/client_cert_identity_test_util.h"
#include "net/ssl/client_cert_store.h"
#include "net/ssl/ssl_private_key.h"
#include "net/test/cert_test_util.h"
#include "net/test/embedded_test_server/default_handlers.h"
#include "net/test/test_data_directory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/boringssl/src/include/openssl/ssl.h"

#if BUILDFLAG(IS_ANDROID)
#include "components/safe_browsing/android/safe_browsing_api_handler_bridge.h"
#include "components/safe_browsing/android/safe_browsing_api_handler_util.h"
#else
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/test/base/ui_test_utils.h"
#endif  // BUILDFLAG(IS_ANDROID)

using ManagementContextMixin = enterprise::test::ManagementContextMixin;
using ManagementContext = enterprise::test::ManagementContext;

namespace em = enterprise_management;

namespace enterprise_reporting {

namespace {

const char* kCookieHeaderName = "Cookie";
const char* kSetCookiePath = "/set-cookie";

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
constexpr net::BackoffEntry::Policy kTestBackoffPolicy = {
    0,   // Number of initial errors to ignore before applying exponential
         // back-off rules.
    10,  // Initial delay in ms.
    2,   // Factor by which the waiting time will be multiplied.
    0,   // Fuzzing percentage.
    -1,  // No maximum delay.
    -1,  // It's up to the caller to reset the backoff time.
    false,
};

class FakeClientCertStore : public net::ClientCertStore {
 public:
  FakeClientCertStore() = default;
  ~FakeClientCertStore() override = default;

  void GetClientCerts(
      scoped_refptr<const net::SSLCertRequestInfo> cert_request_info,
      ClientCertListCallback callback) override {
    net::ClientCertIdentityList cert_identities;
    {
      base::ScopedAllowBlockingForTesting allow_blocking;
      auto identity = net::FakeClientCertIdentity::CreateFromCertAndKeyFiles(
          net::GetTestCertsDirectory(), "client_1.pem", "client_1.pk8");
      if (identity) {
        cert_identities.push_back(std::move(identity));
      }
    }
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE,
        base::BindOnce(std::move(callback), std::move(cert_identities)));
  }
};
#endif

struct CapturedProfileReportRequest {
  std::optional<em::DeviceManagementRequest> request{std::nullopt};
  std::optional<std::string> cookie{std::nullopt};
};

std::string CreateFakeAuthCookieValue() {
  // Set the SAPISID cookie.
  return base::StrCat({GaiaConstants::kGaiaSigninCookieName, "=foo"});
}

std::string CreateOtherFakeAuthCookieValue() {
  // Set the SAPISID cookie.
  return base::StrCat({GaiaConstants::kGaiaSigninCookieName, "=bar"});
}

std::string CreateFakeSerializedAuthCookie(std::string_view cookie_value) {
  // Make sure there are no spaces in this string, as the URL encoding may
  // drop some of the cookie parameters.
  return base::StrCat(
      {cookie_value, ";secure;Domain=.google.com;max-age=1000"});
}

std::string GetSetCookiesPath(std::string_view cookie_value) {
  return base::StrCat(
      {kSetCookiePath, "?", CreateFakeSerializedAuthCookie(cookie_value)});
}

}  // namespace

class SecurityReportingBrowserTest
    : public MixinBasedPlatformBrowserTest,
      public testing::WithParamInterface<testing::tuple<bool, bool, bool>> {
 protected:
  SecurityReportingBrowserTest() {
    std::vector<base::test::FeatureRef> enabled_features;
    std::vector<base::test::FeatureRef> disabled_features;
    enabled_features.push_back(
        enterprise_signals::features::kCertificateCollectionEnabled);
    if (are_policies_enabled()) {
      enabled_features.push_back(
          enterprise_signals::features::kPolicyDataCollectionEnabled);
    } else {
      disabled_features.push_back(
          enterprise_signals::features::kPolicyDataCollectionEnabled);
    }
    scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
    management_mixin_ = ManagementContextMixin::Create(
        &mixin_host_, this,
        {
            .is_cloud_user_managed = true,
            .is_cloud_machine_managed = is_device_managed(),
            .affiliated = is_affiliated(),
        });
  }

  void SetUp() override {
    SetFakeSignalsValues();

    embedded_https_test_server().SetCertHostnames(
        {"m.google.com", "accounts.google.com", "google.com"});
    net::test_server::RegisterDefaultHandlers(&embedded_https_test_server());

    CHECK(embedded_https_test_server().InitializeAndListen());
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
    ReportUploader::SetBackoffPolicyForTesting(&kTestBackoffPolicy);
    create_services_subscription_ =
        BrowserContextDependencyManager::GetInstance()
            ->RegisterCreateServicesCallbackForTesting(
                base::BindRepeating([](content::BrowserContext* context) {
                  Profile* profile = Profile::FromBrowserContext(context);
                  ProfileNetworkContextServiceFactory::GetForContext(profile)
                      ->set_client_cert_store_factory_for_testing(
                          base::BindRepeating(
                              []() -> std::unique_ptr<net::ClientCertStore> {
                                return std::make_unique<FakeClientCertStore>();
                              }));
                }));
#endif
    MixinBasedPlatformBrowserTest::SetUp();
  }

  void TearDown() override {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
    ReportUploader::SetBackoffPolicyForTesting(nullptr);
#endif
    MixinBasedPlatformBrowserTest::TearDown();
  }

  void SetUpCommandLine(base::CommandLine* command_line) override {
    command_line->AppendSwitchASCII(
        policy::switches::kDeviceManagementUrl,
        embedded_https_test_server()
            .GetURL("m.google.com", "/devicemanagement/data/api")
            .spec());
  }

  void SetUpInProcessBrowserTestFixture() override {
    policy::ChromeBrowserPolicyConnector::EnableCommandLineSupportForTesting();
    MixinBasedPlatformBrowserTest::SetUpInProcessBrowserTestFixture();
  }

  void SetUpOnMainThread() override {
    host_resolver()->AddRule("*", "127.0.0.1");
    embedded_https_test_server().RegisterRequestHandler(base::BindRepeating(
        &SecurityReportingBrowserTest::HandleRequest, base::Unretained(this)));

    embedded_https_test_server().StartAcceptingConnections();

    MixinBasedPlatformBrowserTest::SetUpOnMainThread();

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
    ProfileNetworkContextServiceFactory::GetForContext(GetProfile())
        ->set_client_cert_store_factory_for_testing(
            base::BindRepeating([]() -> std::unique_ptr<net::ClientCertStore> {
              return std::make_unique<FakeClientCertStore>();
            }));
#endif
  }

  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
      const net::test_server::HttpRequest& request) {
    const auto& request_url = request.GetURL();
    if (request_url.path() != "/devicemanagement/data/api") {
      SCOPED_TRACE("Not a DM server request.");
      return nullptr;
    }

    std::string action_name;
    if (!net::GetValueForKeyInQuery(
            request_url, policy::dm_protocol::kParamRequest, &action_name)) {
      SCOPED_TRACE("No action name.");
      return nullptr;
    }

    if (action_name == policy::dm_protocol::kValueRequestPolicy) {
      return std::make_unique<net::test_server::BasicHttpResponse>();
    }

    if (action_name ==
        policy::dm_protocol::kValueRequestGenerateChromeProfileChallenge) {
      challenge_requests_count_++;
      auto response = std::make_unique<net::test_server::BasicHttpResponse>();
      response->set_code(net::HTTP_OK);
      em::DeviceManagementResponse resp_proto;
      resp_proto.mutable_generate_chrome_profile_challenge_response()
          ->set_challenge(
              base::StrCat({"fake_challenge_bytes_",
                            base::NumberToString(challenge_requests_count_)}));
      response->set_content(resp_proto.SerializeAsString());
      return response;
    }

    if (action_name != policy::dm_protocol::kValueRequestChromeProfileReport) {
      SCOPED_TRACE("Not a Profile report.");
      return nullptr;
    }

    profile_report_requests_count_++;

    if (fail_profile_reports_count_ > 0) {
      fail_profile_reports_count_--;
      auto response = std::make_unique<net::test_server::BasicHttpResponse>();
      response->set_code(net::HTTP_INTERNAL_SERVER_ERROR);
      return response;
    }

    if (!pending_capture_.is_null()) {
      CapturedProfileReportRequest captured_request{};
      auto cookie_it = request.headers.find(kCookieHeaderName);
      if (cookie_it != request.headers.end()) {
        captured_request.cookie = cookie_it->second;
      }

      em::DeviceManagementRequest request_proto;
      if (request_proto.ParseFromString(request.content)) {
        captured_request.request = std::move(request_proto);
      }

      std::move(pending_capture_).Run(std::move(captured_request));
    }

    return std::make_unique<net::test_server::BasicHttpResponse>();
  }

  void SetFakeCookieValue() { SetFakeCookieValue(CreateFakeAuthCookieValue()); }

  void SetOtherFakeCookieValue() {
    SetFakeCookieValue(CreateOtherFakeAuthCookieValue());
  }

  // Verifies the request's content and auth values.
  void VerifyRequest(const CapturedProfileReportRequest& request,
                     em::ChromeProfileReportRequest::ReportType profile_type,
                     std::optional<std::string> cookie_value = std::nullopt) {
    ASSERT_TRUE(request.request);
    ASSERT_TRUE(request.request->has_chrome_profile_report_request());
    EXPECT_EQ(request.cookie, cookie_value);

    const auto& profile_report_request =
        request.request->chrome_profile_report_request();
    EXPECT_EQ(profile_report_request.report_type(), profile_type);

    bool expect_signals_override_value =
        profile_type != em::ChromeProfileReportRequest::PROFILE_REPORT;
    bool expect_base_profile_report =
        profile_type !=
        em::ChromeProfileReportRequest::PROFILE_SECURITY_SIGNALS;

    EXPECT_EQ(profile_report_request.has_browser_device_identifier(),
              expect_signals_override_value);

    if (expect_signals_override_value) {
      auto browser_device_identifier =
          profile_report_request.browser_device_identifier();
      VerifyDeviceIdentifier(std::ref(browser_device_identifier),
                             can_collect_pii_signals());
    }

    ASSERT_TRUE(profile_report_request.has_os_report());
    VerifyOsReportSignals(profile_report_request.os_report(),
                          expect_signals_override_value,
                          can_collect_pii_signals());

    ASSERT_TRUE(profile_report_request.has_browser_report());
    auto browser_report = profile_report_request.browser_report();
    EXPECT_EQ(browser_report.browser_version(),
              version_info::GetVersionNumber());

    EXPECT_EQ(1, browser_report.chrome_user_profile_infos_size());
    auto chrome_user_profile_info = browser_report.chrome_user_profile_infos(0);

    // `profile_signals_report` is a signals report only sub-proto.
    EXPECT_EQ(chrome_user_profile_info.has_profile_signals_report(),
              expect_signals_override_value);
    EXPECT_EQ(chrome_user_profile_info.profile_id().empty(),
              !expect_signals_override_value);

    if (expect_signals_override_value) {
      VerifyProfileSignalsReport(
          chrome_user_profile_info.profile_signals_report(), GetProfile());

      EXPECT_EQ(chrome_user_profile_info.profile_id(),
                enterprise::ProfileIdServiceFactory::GetForProfile(GetProfile())
                    ->GetProfileId()
                    .value());
    }

    if (expect_base_profile_report || are_policies_enabled()) {
      EXPECT_GT(chrome_user_profile_info.chrome_policies_size(), 0);
    } else {
      EXPECT_EQ(chrome_user_profile_info.chrome_policies_size(), 0);
    }
  }

  bool is_device_managed() { return testing::get<0>(GetParam()); }
  bool is_affiliated() { return testing::get<1>(GetParam()); }
  bool are_policies_enabled() { return testing::get<2>(GetParam()); }

  bool can_collect_pii_signals() {
    return is_device_managed() && is_affiliated();
  }

  int challenge_requests_count() const { return challenge_requests_count_; }
  void clear_challenge_requests_count() { challenge_requests_count_ = 0; }

  int profile_report_requests_count() const {
    return profile_report_requests_count_;
  }
  void clear_profile_report_requests_count() {
    profile_report_requests_count_ = 0;
  }

  void set_fail_profile_reports_count(int count) {
    fail_profile_reports_count_ = count;
  }

  ManagementContextMixin* management_mixin() { return management_mixin_.get(); }

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

  base::OnceCallback<void(CapturedProfileReportRequest)> pending_capture_;

  void SetFakeCookieValue(std::string_view cookie_value) {
#if BUILDFLAG(IS_ANDROID)
    ASSERT_TRUE(content::NavigateToURL(
        chrome_test_utils::GetActiveWebContents(this),
        embedded_https_test_server().GetURL("accounts.google.com",
                                            GetSetCookiesPath(cookie_value))));
#else
    ASSERT_TRUE(ui_test_utils::NavigateToURL(
        browser(),
        embedded_https_test_server().GetURL("accounts.google.com",
                                            GetSetCookiesPath(cookie_value))));
#endif  // BUILDFLAG(IS_ANDROID)
  }

 private:
  int challenge_requests_count_ = 0;
  int profile_report_requests_count_ = 0;
  int fail_profile_reports_count_ = 0;
  base::test::ScopedFeatureList scoped_feature_list_;
  base::HistogramTester histogram_tester_;
  std::unique_ptr<ManagementContextMixin> management_mixin_;
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
  base::CallbackListSubscription create_services_subscription_;
#endif
};

// Tests that a security-only report is sent when only the security reports
// user policy is enabled. It should also not include the cookie, as the
// authenticated reporting policy is not set.
IN_PROC_BROWSER_TEST_P(SecurityReportingBrowserTest, SecurityReportOnly) {
  clear_challenge_requests_count();
  SetFakeCookieValue();

  base::test::TestFuture<CapturedProfileReportRequest> test_future;
  pending_capture_ =
      base::BindPostTask(base::SequencedTaskRunner::GetCurrentDefault(),
                         test_future.GetCallback());

  base::flat_map<std::string, std::optional<base::Value>> policy_values;
  policy_values.insert(
      {policy::key::kUserSecuritySignalsReporting, base::Value(true)});
  management_mixin()->SetCloudUserPolicies(std::move(policy_values));

  CapturedProfileReportRequest captured_request = test_future.Get();
  VerifyRequest(captured_request,
                em::ChromeProfileReportRequest::PROFILE_SECURITY_SIGNALS);
  EXPECT_EQ(challenge_requests_count(), 0);

  ASSERT_TRUE(captured_request.request);
  ASSERT_TRUE(captured_request.request->has_chrome_profile_report_request());
  const auto& profile_report =
      captured_request.request->chrome_profile_report_request();
  ASSERT_TRUE(profile_report.has_browser_report());
  ASSERT_EQ(1,
            profile_report.browser_report().chrome_user_profile_infos_size());
  const auto& user_profile_info =
      profile_report.browser_report().chrome_user_profile_infos(0);
  EXPECT_FALSE(user_profile_info.has_certificates_were_truncated());
  EXPECT_EQ(user_profile_info.certificates_size(), 0);
}

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
// Tests that when SecuritySignalsClientCertificatesSelectors policy and
// certificate collection feature flag are set, security report is successfully
// generated.
IN_PROC_BROWSER_TEST_P(SecurityReportingBrowserTest,
                       SecurityReportWithCertificatesSelectors) {
  clear_challenge_requests_count();
  SetFakeCookieValue();

  base::test::TestFuture<CapturedProfileReportRequest> test_future;
  pending_capture_ =
      base::BindPostTask(base::SequencedTaskRunner::GetCurrentDefault(),
                         test_future.GetCallback());

  base::ListValue policy_list;
  base::DictValue selector;
  base::DictValue issuer;
  issuer.Set("CN", "B CA");
  selector.Set("ISSUER", std::move(issuer));
  policy_list.Append(std::move(selector));

  base::flat_map<std::string, std::optional<base::Value>> policy_values;
  policy_values.insert(
      {policy::key::kUserSecuritySignalsReporting, base::Value(true)});
  policy_values.insert(
      {policy::key::kSecuritySignalsClientCertificatesSelectors,
       base::Value(std::move(policy_list))});
  management_mixin()->SetCloudUserPolicies(std::move(policy_values));

  CapturedProfileReportRequest captured_request = test_future.Get();
  VerifyRequest(captured_request,
                em::ChromeProfileReportRequest::PROFILE_SECURITY_SIGNALS);
  EXPECT_GT(challenge_requests_count(), 0);

  ASSERT_TRUE(captured_request.request);
  ASSERT_TRUE(captured_request.request->has_chrome_profile_report_request());
  const auto& profile_report =
      captured_request.request->chrome_profile_report_request();
  ASSERT_TRUE(profile_report.has_browser_report());
  ASSERT_EQ(1,
            profile_report.browser_report().chrome_user_profile_infos_size());
  const auto& user_profile_info =
      profile_report.browser_report().chrome_user_profile_infos(0);
  if (can_collect_pii_signals()) {
    EXPECT_TRUE(user_profile_info.has_certificates_were_truncated());
    EXPECT_EQ(1, user_profile_info.certificates_size());
    EXPECT_FALSE(user_profile_info.certificates(0).data().empty());
    EXPECT_FALSE(user_profile_info.certificates(0).signature().empty());
  } else {
    EXPECT_FALSE(user_profile_info.has_certificates_were_truncated());
    EXPECT_EQ(0, user_profile_info.certificates_size());
  }
}

// Tests that when an upload attempt for a report with certificate signals fails
// with a transient error, the scheduler retries the upload with a newly
// generated challenge and successfully completes the upload.
IN_PROC_BROWSER_TEST_P(SecurityReportingBrowserTest,
                       SecurityReportWithCertificates_RetryOnTransientError) {
  clear_challenge_requests_count();
  clear_profile_report_requests_count();
  set_fail_profile_reports_count(1);
  SetFakeCookieValue();

  base::test::TestFuture<CapturedProfileReportRequest> test_future;
  pending_capture_ =
      base::BindPostTask(base::SequencedTaskRunner::GetCurrentDefault(),
                         test_future.GetCallback());

  base::ListValue policy_list;
  base::DictValue selector;
  base::DictValue issuer;
  issuer.Set("CN", "B CA");
  selector.Set("ISSUER", std::move(issuer));
  policy_list.Append(std::move(selector));

  base::flat_map<std::string, std::optional<base::Value>> policy_values;
  policy_values.insert(
      {policy::key::kUserSecuritySignalsReporting, base::Value(true)});
  policy_values.insert(
      {policy::key::kSecuritySignalsClientCertificatesSelectors,
       base::Value(std::move(policy_list))});
  management_mixin()->SetCloudUserPolicies(std::move(policy_values));

  CapturedProfileReportRequest captured_request = test_future.Get();
  VerifyRequest(captured_request,
                em::ChromeProfileReportRequest::PROFILE_SECURITY_SIGNALS);

  // Verifies that 2 challenge requests were made (one for initial attempt,
  // one for retry).
  EXPECT_EQ(challenge_requests_count(), 2);
  // Verifies that 2 profile report requests were sent (one failed, one
  // succeeded).
  EXPECT_EQ(profile_report_requests_count(), 2);

  ASSERT_TRUE(captured_request.request);
  ASSERT_TRUE(captured_request.request->has_chrome_profile_report_request());
  const auto& profile_report =
      captured_request.request->chrome_profile_report_request();
  ASSERT_TRUE(profile_report.has_browser_report());
  ASSERT_EQ(1,
            profile_report.browser_report().chrome_user_profile_infos_size());
  const auto& user_profile_info =
      profile_report.browser_report().chrome_user_profile_infos(0);
  if (can_collect_pii_signals()) {
    EXPECT_TRUE(user_profile_info.has_certificates_were_truncated());
    EXPECT_EQ(1, user_profile_info.certificates_size());
    EXPECT_FALSE(user_profile_info.certificates(0).data().empty());
    EXPECT_FALSE(user_profile_info.certificates(0).signature().empty());
  } else {
    EXPECT_FALSE(user_profile_info.has_certificates_were_truncated());
    EXPECT_EQ(0, user_profile_info.certificates_size());
  }
}
#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)

// Tests that a combined Profile report is sent when all user
// policies are enabled, with cookies.
IN_PROC_BROWSER_TEST_P(SecurityReportingBrowserTest, CombineReportWithAuth) {
  SetFakeCookieValue();

  base::test::TestFuture<CapturedProfileReportRequest> test_future;
  pending_capture_ =
      base::BindPostTask(base::SequencedTaskRunner::GetCurrentDefault(),
                         test_future.GetCallback());

  base::flat_map<std::string, std::optional<base::Value>> policy_values;
  policy_values.insert(
      {policy::key::kCloudProfileReportingEnabled, base::Value(true)});
  policy_values.insert(
      {policy::key::kUserSecuritySignalsReporting, base::Value(true)});
  policy_values.insert(
      {policy::key::kUserSecurityAuthenticatedReporting, base::Value(true)});
  management_mixin()->SetCloudUserPolicies(std::move(policy_values));

  VerifyRequest(
      test_future.Get(),
      em::ChromeProfileReportRequest::PROFILE_REPORT_WITH_SECURITY_SIGNALS,
      CreateFakeAuthCookieValue());
}

// Tests that a standard Profile report is sent when only its user
// policy is enabled.
IN_PROC_BROWSER_TEST_P(SecurityReportingBrowserTest, OnlyProfileReport) {
  SetFakeCookieValue();

  base::test::TestFuture<CapturedProfileReportRequest> test_future;
  pending_capture_ =
      base::BindPostTask(base::SequencedTaskRunner::GetCurrentDefault(),
                         test_future.GetCallback());

  base::flat_map<std::string, std::optional<base::Value>> policy_values;
  policy_values.insert(
      {policy::key::kCloudProfileReportingEnabled, base::Value(true)});
  management_mixin()->SetCloudUserPolicies(std::move(policy_values));

  VerifyRequest(test_future.Get(),
                em::ChromeProfileReportRequest::PROFILE_REPORT);
}

INSTANTIATE_TEST_SUITE_P(
    ManagedDeviceCase,
    SecurityReportingBrowserTest,
    testing::Combine(/*is_device_managed=*/testing::Values(true),
                     /*is_affiliated=*/testing::Bool(),
                     /*are_policies_enabled=*/testing::Bool()));

INSTANTIATE_TEST_SUITE_P(
    UnmanagedDeviceCase,
    SecurityReportingBrowserTest,
    testing::Combine(/*is_device_managed=*/testing::Values(false),
                     /*is_affiliated=*/testing::Values(false),
                     /*are_policies_enabled=*/testing::Bool()));

// Test that confirms the correct form of reports are being triggered.
// Collection contexts such as management state don't affect the expectations so
// we don't need to covered them with redundant test cases.
class SecurityReportTriggerBrowserTest : public SecurityReportingBrowserTest {
 protected:
  SecurityReportTriggerBrowserTest() : SecurityReportingBrowserTest() {}
};

// Tests that a security-only report is sent when only the security reports
// user policy is enabled. It should include the cookie, as the authenticated
// reporting policy is enabled. Updating the cookie again should also trigger
// another report.
IN_PROC_BROWSER_TEST_P(SecurityReportTriggerBrowserTest,
                       SecurityReportWithAuth_Reauth) {
  SetFakeCookieValue();

  base::test::TestFuture<CapturedProfileReportRequest> test_future;
  pending_capture_ =
      base::BindPostTask(base::SequencedTaskRunner::GetCurrentDefault(),
                         test_future.GetCallback());

  base::flat_map<std::string, std::optional<base::Value>> policy_values;
  policy_values.insert(
      {policy::key::kUserSecuritySignalsReporting, base::Value(true)});
  policy_values.insert(
      {policy::key::kUserSecurityAuthenticatedReporting, base::Value(true)});
  management_mixin()->SetCloudUserPolicies(std::move(policy_values));

  VerifyRequest(test_future.Get(),
                em::ChromeProfileReportRequest::PROFILE_SECURITY_SIGNALS,
                CreateFakeAuthCookieValue());

  // Commented-out, as the cookies are not set for "google.com", so our
  // trigger never kicks-in. Verify that another request will be uploaded if
  // the auth cookie changes.
  test_future.Clear();

  pending_capture_ =
      base::BindPostTask(base::SequencedTaskRunner::GetCurrentDefault(),
                         test_future.GetCallback());

  SetOtherFakeCookieValue();

  VerifyRequest(test_future.Get(),
                em::ChromeProfileReportRequest::PROFILE_SECURITY_SIGNALS,
                CreateOtherFakeAuthCookieValue());
}

// Tests that a standard Profile report is sent when only its user
// policy is enabled. Also sets the authentication policy, but the
// cookie should not be forwarded, as that policy only works with
// reports containing security information.
IN_PROC_BROWSER_TEST_P(SecurityReportTriggerBrowserTest,
                       OnlyProfileReportWithAuth_NoCookie) {
  SetFakeCookieValue();

  base::test::TestFuture<CapturedProfileReportRequest> test_future;
  pending_capture_ =
      base::BindPostTask(base::SequencedTaskRunner::GetCurrentDefault(),
                         test_future.GetCallback());

  base::flat_map<std::string, std::optional<base::Value>> policy_values;
  policy_values.insert(
      {policy::key::kCloudProfileReportingEnabled, base::Value(true)});
  policy_values.insert(
      {policy::key::kUserSecurityAuthenticatedReporting, base::Value(true)});
  management_mixin()->SetCloudUserPolicies(std::move(policy_values));

  VerifyRequest(test_future.Get(),
                em::ChromeProfileReportRequest::PROFILE_REPORT);
}

// Tests that a security-only report is sent and that a policy change report
// gets triggered on first profile launch. Also tests that when policies are
// updated another report is uploaded with the policy change trigger.
IN_PROC_BROWSER_TEST_P(SecurityReportTriggerBrowserTest,
                       SecurityReportFromPolicyUpdate) {
  SetFakeCookieValue();

  // Timer trigger report that occurs on initial startup.
  base::test::TestFuture<CapturedProfileReportRequest> test_future;
  pending_capture_ =
      base::BindPostTask(base::SequencedTaskRunner::GetCurrentDefault(),
                         test_future.GetCallback());

  base::flat_map<std::string, std::optional<base::Value>> policy_values;
  policy_values.insert(
      {policy::key::kUserSecuritySignalsReporting, base::Value(true)});
  policy_values.insert(
      {policy::key::kSavingBrowserHistoryDisabled, base::Value(false)});
  management_mixin()->SetCloudUserPolicies(std::move(policy_values));

  VerifyRequest(test_future.Get(),
                em::ChromeProfileReportRequest::PROFILE_SECURITY_SIGNALS);

  if (!are_policies_enabled()) {
    histogram_tester().ExpectBucketCount(
        "Enterprise.SecurityReport.User.Trigger", SecurityReportTrigger::kTimer,
        1);
    histogram_tester().ExpectBucketCount(
        "Enterprise.SecurityReport.User.Trigger",
        SecurityReportTrigger::kPolicyChange, 0);
    return;
  }

  // This ensures the Network Service response has time to hop threads
  // and trigger the "OnReportUploaded" callback.
  {
    base::RunLoop loop;
    base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
        FROM_HERE, loop.QuitClosure(), base::Seconds(1));
    loop.Run();
  }

  // Policy update trigger report that occurs when policies are updated.
  test_future.Clear();
  pending_capture_ =
      base::BindPostTask(base::SequencedTaskRunner::GetCurrentDefault(),
                         test_future.GetCallback());

  base::flat_map<std::string, std::optional<base::Value>> policy_values2;
  policy_values2.insert(
      {policy::key::kUserSecuritySignalsReporting, base::Value(true)});
  policy_values2.insert(
      {policy::key::kSavingBrowserHistoryDisabled, base::Value(true)});
  management_mixin()->SetCloudUserPolicies(std::move(policy_values2));

  VerifyRequest(test_future.Get(),
                em::ChromeProfileReportRequest::PROFILE_SECURITY_SIGNALS);

  histogram_tester().ExpectBucketCount("Enterprise.SecurityReport.User.Trigger",
                                       SecurityReportTrigger::kTimer, 1);
  histogram_tester().ExpectBucketCount("Enterprise.SecurityReport.User.Trigger",
                                       SecurityReportTrigger::kPolicyChange, 2);
}

INSTANTIATE_TEST_SUITE_P(
    All,
    SecurityReportTriggerBrowserTest,
    testing::Combine(/*is_device_managed*/ ::testing::Values(false),
                     /*is_affiliated*/ ::testing::Values(false),
                     /*are_policies_enabled=*/testing::Bool()));

}  // namespace enterprise_reporting
