// 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.

#ifndef COMPONENTS_SYNC_PREFERENCES_CROSS_DEVICE_PREF_TRACKER_CROSS_DEVICE_PREF_TRACKER_H_
#define COMPONENTS_SYNC_PREFERENCES_CROSS_DEVICE_PREF_TRACKER_CROSS_DEVICE_PREF_TRACKER_H_

#include <optional>
#include <string>
#include <string_view>
#include <vector>

#include "base/observer_list_types.h"
#include "build/build_config.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/sync_device_info/device_info.h"
#include "components/sync_preferences/cross_device_pref_tracker/timestamped_pref_value.h"

#if BUILDFLAG(IS_ANDROID)
#include "base/android/scoped_java_ref.h"
#endif  // BUILDFLAG(IS_ANDROID)

namespace sync_preferences {

// Abstract interface for a keyed service responsible for querying the values of
// select non-syncing prefs across all of a user's syncing devices. It allows
// clients to observe how a particular non-syncing pref value differs across
// platforms and form factors.
//
// It works by tracking a device-specific pref (e.g., "ios.example_pref") and
// storing its value in a corresponding shared, syncable dictionary pref, known
// as the cross-device pref. The cross-device pref's name is deterministically
// generated by prepending "cross_device." to the tracked pref's name (e.g.,
// "cross_device.ios.example_pref").
//
// This system can also be used to expose platform-specific pref data to other
// platforms that may not have a corresponding pref. However, for most clients,
// if the same pref is used on multiple platforms, it should be defined in
// `CrossDevicePrefProvider`.
//
// For more details on the design, see go/cross-device-pref-tracker.
class CrossDevicePrefTracker : public KeyedService {
 public:
  // The current service status for `CrossDevicePrefTracker`.
  //
  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  //
  // GENERATED_JAVA_ENUM_PACKAGE: (
  // org.chromium.components.sync_preferences.cross_device_pref_tracker)
  // LINT.IfChange(ServiceStatus)
  enum class ServiceStatus {
    // The tracker is fully operational and available for use.
    kAvailable = 0,
    // `DeviceInfoTracker` is not available.
    kDeviceInfoTrackerMissing = 1,
    // Sync is not configured for writes (e.g., user is signed out or Prefs sync
    // disabled).
    kSyncNotConfigured = 2,
    // `LocalDeviceInfo` (Cache GUID) is not yet initialized.
    kLocalDeviceInfoMissing = 3,
    // `LocalDeviceInfo` is missing and Sync is not configured for writes.
    kSyncNotConfiguredAndLocalDeviceInfoMissing = 4,
    // Sync is configured but initial preferences have not been downloaded.
    kWaitingForInitialSync = 5,
    kMaxValue = kWaitingForInitialSync,
  };
  // Note: The UMA XML enum is named "CrossDevicePrefTrackerAvailabilityAtQuery"
  // for legacy reasons.
  // LINT.ThenChange(/tools/metrics/histograms/metadata/sync/enums.xml:CrossDevicePrefTrackerAvailabilityAtQuery)

  // Observer interface for remote changes.
  class Observer : public base::CheckedObserver {
   public:
    // Called when `pref_name` is updated to `pref_value` on a remote device.
    // The `pref_name` reported here is always the tracked pref name (e.g.,
    // "ios.example_pref").
    virtual void OnRemotePrefChanged(
        std::string_view pref_name,
        const TimestampedPrefValue& pref_value,
        const syncer::DeviceInfo& remote_device_info) {}
    // Called when the service status changes (e.g., the tracker becomes
    // available to use).
    virtual void OnServiceStatusChanged(ServiceStatus status) {}
  };

  // Defines criteria for querying devices.
  struct DeviceFilter {
    std::optional<syncer::DeviceInfo::OsType> os_type;
    std::optional<syncer::DeviceInfo::FormFactor> form_factor;
    // If provided, only include devices whose
    // `DeviceInfo::last_updated_timestamp` is within this duration from the
    // time the filter is applied.
    std::optional<base::TimeDelta> max_sync_recency;
  };

  ~CrossDevicePrefTracker() override;

  virtual void AddObserver(Observer* observer) = 0;
  virtual void RemoveObserver(Observer* observer) = 0;

  // Returns the current status of the service. (Clients can use this to
  // determine if they should wait to query the tracker until it is
  // `kAvailable`.)
  virtual ServiceStatus GetServiceStatus() const = 0;

  // Retrieves all values for a tracked pref matching the filter, sorted in
  // descending order by timestamp (i.e., most recent first).
  // `pref_name` can be either the tracked pref name (e.g.,
  // "ios.example_pref") or the cross-device pref name (e.g.,
  // "cross_device.ios.example_pref").
  virtual std::vector<TimestampedPrefValue> GetValues(
      std::string_view pref_name,
      const DeviceFilter& filter) const = 0;

  // Convenience wrapper to get the single most recent value.
  // `pref_name` can be either the tracked pref name or the cross-device pref
  // name.
  //
  // NOTE: In the case of a timestamp collision, we'll use the value of the
  // device that's most recently updated with the sync servers (via
  // `DeviceInfo::last_updated_timestamp`).
  virtual std::optional<TimestampedPrefValue> GetMostRecentValue(
      std::string_view pref_name,
      const DeviceFilter& filter) const = 0;

#if BUILDFLAG(IS_ANDROID)
  // Return the Java object that allows access to the CrossDevicePrefTracker.
  virtual base::android::ScopedJavaLocalRef<jobject> GetJavaObject() = 0;
  // Returns the current status of the service.
  virtual int GetServiceStatus(JNIEnv* env) const = 0;
  // Java versions of query methods.
  // `pref_name` can be either the tracked pref name or the cross-device pref
  // name.
  virtual base::android::ScopedJavaLocalRef<jobjectArray> GetValues(
      JNIEnv* env,
      const base::android::JavaRef<jstring>& pref_name,
      std::optional<int> os_type,
      std::optional<int> form_factor,
      std::optional<int64_t> max_sync_recency_microseconds) const = 0;
  // `pref_name` can be either the tracked pref name or the cross-device pref
  // name.
  virtual base::android::ScopedJavaLocalRef<jobject> GetMostRecentValue(
      JNIEnv* env,
      const base::android::JavaRef<jstring>& pref_name,
      std::optional<int> os_type,
      std::optional<int> form_factor,
      std::optional<int64_t> max_sync_recency_microseconds) const = 0;
#endif  // BUILDFLAG(IS_ANDROID)

 protected:
  CrossDevicePrefTracker() = default;
};

}  // namespace sync_preferences

#endif  // COMPONENTS_SYNC_PREFERENCES_CROSS_DEVICE_PREF_TRACKER_CROSS_DEVICE_PREF_TRACKER_H_
