// 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 <memory>
#include <string>
#include <vector>

#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/memory/weak_ptr.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/browser/metrics/chrome_metrics_services_manager_client.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
#include "chrome/browser/sync/test/integration/sync_test.h"
#include "components/metrics/delegating_provider.h"
#include "components/metrics/demographics/demographic_metrics_provider.h"
#include "components/metrics/demographics/demographic_metrics_test_utils.h"
#include "components/metrics/metrics_service.h"
#include "components/metrics/metrics_switches.h"
#include "components/metrics_services_manager/metrics_services_manager.h"
#include "components/sync/service/sync_user_settings.h"
#include "content/public/test/browser_test.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
#include "third_party/metrics_proto/user_demographics.pb.h"

namespace metrics {
namespace {

class MetricsServiceUserDemographicsBrowserTest
    : public SyncTest,
      public testing::WithParamInterface<test::DemographicsTestParams> {
 public:
  MetricsServiceUserDemographicsBrowserTest() : SyncTest(SINGLE_CLIENT) {
    if (GetParam().enable_feature) {
      // Enable UMA and reporting of the synced user's birth year and gender.
      scoped_feature_list_.InitWithFeatures(
          // enabled_features =
          {internal::kMetricsReportingFeature, kDemographicMetricsReporting},
          // disabled_features =
          {});
    } else {
      scoped_feature_list_.InitWithFeatures(
          // enabled_features =
          {internal::kMetricsReportingFeature},
          // disabled_features =
          {kDemographicMetricsReporting});
    }
  }

  MetricsServiceUserDemographicsBrowserTest(
      const MetricsServiceUserDemographicsBrowserTest&) = delete;
  MetricsServiceUserDemographicsBrowserTest& operator=(
      const MetricsServiceUserDemographicsBrowserTest&) = delete;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    SyncTest::SetUpCommandLine(command_line);
    // Enable the metrics service for testing (in recording-only mode).
    command_line->AppendSwitch(switches::kMetricsRecordingOnly);
  }

  void SetUp() override {
    // Consent for metrics and crash reporting for testing.
    ChromeMetricsServiceAccessor::SetMetricsAndCrashReportingForTesting(
        &metrics_consent_);
    SyncTest::SetUp();
  }

  PrefService* local_state() { return g_browser_process->local_state(); }

  // Forces a log record to be generated. Returns a copy of the record on
  // success; otherwise, returns nullptr.
  std::unique_ptr<ChromeUserMetricsExtension> GenerateLogRecord() {
    MetricsService* metrics_service =
        g_browser_process->GetMetricsServicesManager()->GetMetricsService();
    test::BuildAndStoreLog(metrics_service);

    if (!test::HasUnsentLogs(metrics_service))
      return nullptr;
    return test::GetLastUmaLog(metrics_service);
  }

 private:
  bool metrics_consent_ = true;

  base::test::ScopedFeatureList scoped_feature_list_;
};


// LINT.IfChange(AddSyncedUserBirthYearAndGenderToProtoData)
IN_PROC_BROWSER_TEST_P(MetricsServiceUserDemographicsBrowserTest,
                       AddSyncedUserBirthYearAndGenderToProtoData) {
  test::DemographicsTestParams param = GetParam();

  base::HistogramTester histogram;

  const base::Time now = base::Time::Now();
  test::UpdateNetworkTime(now, g_browser_process->network_time_tracker());
  const int test_birth_year = test::GetMaximumEligibleBirthYear(now);
  const UserDemographicsProto::Gender test_gender =
      UserDemographicsProto::GENDER_FEMALE;

  // Add the test synced user birth year and gender priority prefs to the sync
  // server data.
  test::AddUserBirthYearAndGenderToSyncServer(GetFakeServer()->AsWeakPtr(),
                                              test_birth_year, test_gender);

  // TODO(crbug.com/40688248): Try to replace the below set-up code with
  // functions from SyncTest.
  Profile* test_profile = ProfileManager::GetLastUsedProfileIfLoaded();

  // Enable sync for the test profile.
  std::unique_ptr<SyncServiceImplHarness> harness =
      SyncServiceImplHarness::Create(
          test_profile, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
  ASSERT_TRUE(harness->SetupSync());

  // Make sure that there is only one Profile to allow reporting the user's
  // birth year and gender.
  ASSERT_EQ(1, num_clients());

  // Generate a log record.
  std::unique_ptr<ChromeUserMetricsExtension> uma_proto = GenerateLogRecord();
  ASSERT_TRUE(uma_proto);

  // Check log content and the histogram.
  if (param.expect_reported_demographics) {
    EXPECT_EQ(test::GetNoisedBirthYear(local_state(), test_birth_year),
              uma_proto->user_demographics().birth_year());
    EXPECT_EQ(test_gender, uma_proto->user_demographics().gender());
    histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
                                 UserDemographicsStatus::kSuccess, 1);
  } else {
    EXPECT_FALSE(uma_proto->has_user_demographics());
    histogram.ExpectTotalCount("UMA.UserDemographics.Status", /*count=*/0);
  }

#if !BUILDFLAG(IS_CHROMEOS)
  // Sign out the user to revoke all refresh tokens. This prevents any posted
  // tasks from successfully fetching an access token during the tear-down
  // phase and crashing on a DCHECK. See crbug.com/40704261 for more details.
  harness->SignOutPrimaryAccount();
#endif  // !BUILDFLAG(IS_CHROMEOS)
}
// LINT.ThenChange(/ios/chrome/browser/metrics/demographics_egtest.mm:AddSyncedUserBirthYearAndGenderToProtoDataEnabled_msudBrowsertest)
// ThenChange(/ios/chrome/browser/metrics/demographics_egtest.mm:AddSyncedUserBirthYearAndGenderToProtoDataDisabled_msudBrowsertest)

#if BUILDFLAG(IS_CHROMEOS)
// Cannot test for the enabled feature on Chrome OS because there are always
// multiple profiles.
static const auto kDemographicsTestParams = testing::Values(
    test::DemographicsTestParams{/*enable_feature=*/false,
                                 /*expect_reported_demographics=*/false});
#else
static const auto kDemographicsTestParams = testing::Values(
    test::DemographicsTestParams{/*enable_feature=*/false,
                                 /*expect_reported_demographics=*/false},
    test::DemographicsTestParams{/*enable_feature=*/true,
                                 /*expect_reported_demographics=*/true});
#endif

INSTANTIATE_TEST_SUITE_P(,
                         MetricsServiceUserDemographicsBrowserTest,
                         kDemographicsTestParams);

}  // namespace
}  // namespace metrics
