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

#include "components/sync/service/glue/sync_transport_data_prefs.h"

#include <memory>
#include <utility>

#include "base/base64.h"
#include "base/feature_list.h"
#include "base/json/values_util.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/signin/public/base/gaia_id_hash.h"
#include "components/sync/base/account_pref_utils.h"
#include "components/sync/base/features.h"
#include "components/sync/base/pref_names.h"
#include "google_apis/gaia/gaia_id.h"

namespace syncer {

namespace {

const char kSyncGaiaId[] = "sync.gaia_id";

// Keys for the `kSyncTransportDataPerAccount` dictionary pref:
const char kSyncCacheGuid[] = "sync.cache_guid";
const char kSyncBirthday[] = "sync.birthday";
const char kSyncBagOfChips[] = "sync.bag_of_chips";
// 64-bit integer serialization of the base::Time when the last sync occurred.
const char kSyncLastSyncedTime[] = "sync.last_synced_time";
// 64-bit integer serialization of the base::Time of the last sync poll.
const char kSyncLastPollTime[] = "sync.last_poll_time";
// 64-bit integer serialization of base::TimeDelta storing poll intervals
// received by the server. For historic reasons, this is called
// "short_poll_interval", but it's not worth the hassle to rename it.
const char kSyncPollInterval[] = "sync.short_poll_interval";

}  // namespace

SyncTransportDataPrefs::SyncTransportDataPrefs(
    PrefService* pref_service,
    const signin::GaiaIdHash& gaia_id_hash)
    : pref_service_(pref_service), gaia_id_hash_(gaia_id_hash) {}

SyncTransportDataPrefs::~SyncTransportDataPrefs() = default;

// static
void SyncTransportDataPrefs::RegisterProfilePrefs(
    PrefRegistrySimple* registry) {
  registry->RegisterStringPref(kSyncGaiaId, std::string());

  registry->RegisterDictionaryPref(
      prefs::internal::kSyncTransportDataPerAccount);
}

void SyncTransportDataPrefs::ClearForCurrentAccount() {
  ClearAccountKeyedPrefValue(pref_service_,
                             prefs::internal::kSyncTransportDataPerAccount,
                             gaia_id_hash_);
}

// static
void SyncTransportDataPrefs::KeepAccountSettingsPrefsOnlyForUsers(
    PrefService* pref_service,
    const std::vector<signin::GaiaIdHash>& available_gaia_ids) {
  KeepAccountKeyedPrefValuesOnlyForUsers(
      pref_service, prefs::internal::kSyncTransportDataPerAccount,
      available_gaia_ids);
}

base::Time SyncTransportDataPrefs::GetLastSyncedTime() const {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  const base::Value* value = GetAccountKeyedPrefDictEntry(
      pref_service_, prefs::internal::kSyncTransportDataPerAccount,
      gaia_id_hash_, kSyncLastSyncedTime);
  return base::ValueToTime(value).value_or(base::Time());
}

void SyncTransportDataPrefs::SetLastSyncedTime(base::Time time) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  SetAccountKeyedPrefDictEntry(
      pref_service_, prefs::internal::kSyncTransportDataPerAccount,
      gaia_id_hash_, kSyncLastSyncedTime, base::TimeToValue(time));
}

base::Time SyncTransportDataPrefs::GetLastPollTime() const {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  const base::Value* value = GetAccountKeyedPrefDictEntry(
      pref_service_, prefs::internal::kSyncTransportDataPerAccount,
      gaia_id_hash_, kSyncLastPollTime);
  return base::ValueToTime(value).value_or(base::Time());
}

void SyncTransportDataPrefs::SetLastPollTime(base::Time time) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  SetAccountKeyedPrefDictEntry(
      pref_service_, prefs::internal::kSyncTransportDataPerAccount,
      gaia_id_hash_, kSyncLastPollTime, base::TimeToValue(time));
}

base::TimeDelta SyncTransportDataPrefs::GetPollInterval() const {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  const base::Value* value = GetAccountKeyedPrefDictEntry(
      pref_service_, prefs::internal::kSyncTransportDataPerAccount,
      gaia_id_hash_, kSyncPollInterval);
  base::TimeDelta poll_interval =
      base::ValueToTimeDelta(value).value_or(base::TimeDelta());

  // If the poll interval is unreasonably short, reset it. This will cause
  // callers to use a reasonable default value instead.
  // This fixes a past bug where stored pref values were accidentally
  // re-interpreted from "seconds" to "microseconds"; see crbug.com/1246850.
  if (poll_interval < base::Minutes(1)) {
    return base::TimeDelta();
  }
  return poll_interval;
}

void SyncTransportDataPrefs::SetPollInterval(base::TimeDelta interval) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  SetAccountKeyedPrefDictEntry(
      pref_service_, prefs::internal::kSyncTransportDataPerAccount,
      gaia_id_hash_, kSyncPollInterval, base::TimeDeltaToValue(interval));
}

void SyncTransportDataPrefs::SetCurrentSyncingGaiaId(const GaiaId& gaia_id) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  pref_service_->SetString(kSyncGaiaId, gaia_id.ToString());
}

GaiaId SyncTransportDataPrefs::GetCurrentSyncingGaiaId() const {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  return GaiaId(pref_service_->GetString(kSyncGaiaId));
}

void SyncTransportDataPrefs::ClearCurrentSyncingGaiaId() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  pref_service_->ClearPref(kSyncGaiaId);
}

// static
bool SyncTransportDataPrefs::HasCurrentSyncingGaiaId(
    const PrefService* pref_service) {
  return !pref_service->GetString(kSyncGaiaId).empty();
}

// static
void SyncTransportDataPrefs::ClearCurrentSyncingGaiaId(
    PrefService* pref_service) {
  pref_service->ClearPref(kSyncGaiaId);
}

// static
base::flat_set<std::string>
SyncTransportDataPrefs::GetCurrentDeviceCacheGuidsForAllGaiaIds(
    const PrefService* pref_service) {
  std::vector<std::string> result;
  const base::DictValue& data_per_account =
      pref_service->GetDict(prefs::internal::kSyncTransportDataPerAccount);
  for (const auto [gaia, data] : data_per_account) {
    if (!data.is_dict()) {
      continue;
    }
    const std::string* cache_guid = data.GetDict().FindString(kSyncCacheGuid);
    if (cache_guid && !cache_guid->empty()) {
      result.push_back(*cache_guid);
    }
  }
  // Implicit transform to `base::flat_set<std::string>`.
  return result;
}

void SyncTransportDataPrefs::SetCacheGuid(const std::string& cache_guid) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  SetAccountKeyedPrefDictEntry(
      pref_service_, prefs::internal::kSyncTransportDataPerAccount,
      gaia_id_hash_, kSyncCacheGuid, base::Value(cache_guid));
}

std::string SyncTransportDataPrefs::GetCacheGuid() const {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  const base::Value* value = GetAccountKeyedPrefDictEntry(
      pref_service_, prefs::internal::kSyncTransportDataPerAccount,
      gaia_id_hash_, kSyncCacheGuid);
  return value && value->is_string() ? value->GetString() : std::string();
}

void SyncTransportDataPrefs::SetBirthday(const std::string& birthday) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  SetAccountKeyedPrefDictEntry(
      pref_service_, prefs::internal::kSyncTransportDataPerAccount,
      gaia_id_hash_, kSyncBirthday, base::Value(birthday));
}

std::string SyncTransportDataPrefs::GetBirthday() const {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  const base::Value* value = GetAccountKeyedPrefDictEntry(
      pref_service_, prefs::internal::kSyncTransportDataPerAccount,
      gaia_id_hash_, kSyncBirthday);
  return value && value->is_string() ? value->GetString() : std::string();
}

void SyncTransportDataPrefs::SetBagOfChips(const std::string& bag_of_chips) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  // `bag_of_chips` contains a serialized proto which is not utf-8, hence
  // we use base64 encoding in prefs.
  std::string encoded = base::Base64Encode(bag_of_chips);
  SetAccountKeyedPrefDictEntry(
      pref_service_, prefs::internal::kSyncTransportDataPerAccount,
      gaia_id_hash_, kSyncBagOfChips, base::Value(encoded));
}

std::string SyncTransportDataPrefs::GetBagOfChips() const {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  std::string encoded;
  const base::Value* value = GetAccountKeyedPrefDictEntry(
      pref_service_, prefs::internal::kSyncTransportDataPerAccount,
      gaia_id_hash_, kSyncBagOfChips);
  if (value && value->is_string()) {
    encoded = value->GetString();
  }

  // `kSyncBagOfChips` gets stored in base64 because it represents a serialized
  // proto which is not utf-8 encoding.
  std::string decoded;
  base::Base64Decode(encoded, &decoded);
  return decoded;
}

}  // namespace syncer
