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

#ifndef CHROME_BROWSER_ANDROID_WEBAPK_WEBAPK_SYNC_BRIDGE_H_
#define CHROME_BROWSER_ANDROID_WEBAPK_WEBAPK_SYNC_BRIDGE_H_

#include <memory>
#include <optional>

#include "base/memory/weak_ptr.h"
#include "base/time/clock.h"
#include "chrome/browser/android/webapk/webapk_database.h"
#include "chrome/browser/android/webapk/webapk_specifics_fetcher.h"
#include "components/sync/model/data_type_sync_bridge.h"
#include "components/sync/model/entity_change.h"
#include "components/sync/protocol/web_apk_specifics.pb.h"

namespace syncer {
class DataTypeLocalChangeProcessor;
class DataTypeStoreService;
struct EntityData;
class MetadataChangeList;
class ModelError;
}  // namespace syncer

namespace webapk {

struct WebApkRestoreData;

// A unified sync and storage controller.
//
// While webapk::Registry is a read-only model, WebApkSyncBridge is a
// controller for that model. WebApkSyncBridge is responsible for:
// - Registry initialization (reading model from a persistent storage like
// LevelDb or prefs).
// - Writing all the registry updates to a persistent store and sync.
//
// WebApkSyncBridge is the key class to support integration with Unified Sync
// and Storage (USS) system. The sync bridge exclusively owns
// DataTypeLocalChangeProcessor and WebApkDatabase (the storage).
class WebApkSyncBridge : public syncer::DataTypeSyncBridge {
 public:
  WebApkSyncBridge(syncer::DataTypeStoreService* data_type_store_service,
                   base::OnceClosure on_initialized);
  // Tests may inject mocks using this ctor.
  WebApkSyncBridge(
      syncer::DataTypeStoreService* data_type_store_service,
      base::OnceClosure on_initialized,
      std::unique_ptr<syncer::DataTypeLocalChangeProcessor> change_processor,
      std::unique_ptr<base::Clock> clock,
      std::unique_ptr<AbstractWebApkSpecificsFetcher> specifics_fetcher);
  WebApkSyncBridge(const WebApkSyncBridge&) = delete;
  WebApkSyncBridge& operator=(const WebApkSyncBridge&) = delete;
  ~WebApkSyncBridge() override;

  using CommitCallback = base::OnceCallback<void(bool success)>;

  // syncer::DataTypeSyncBridge:
  std::optional<syncer::ModelError> MergeFullSyncData(
      std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
      syncer::EntityChangeList entity_changes) override;
  std::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
      std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
      syncer::EntityChangeList entity_changes) override;
  std::unique_ptr<syncer::DataBatch> GetDataForCommit(
      StorageKeyList storage_keys) override;
  std::unique_ptr<syncer::DataBatch> GetAllDataForDebugging() override;
  std::string GetClientTag(
      const syncer::EntityData& entity_data) const override;
  std::string GetStorageKey(
      const syncer::EntityData& entity_data) const override;
  sync_pb::EntitySpecifics TrimAllSupportedFieldsFromRemoteSpecifics(
      const sync_pb::EntitySpecifics& entity_specifics) const override;
  bool IsEntityDataValid(const syncer::EntityData& entity_data) const override;
  void ApplyDisableSyncChanges(std::unique_ptr<syncer::MetadataChangeList>
                                   delete_metadata_change_list) override;

  void RemoveOldWebAPKsFromSync(int64_t current_time_ms_since_unix_epoch);

  void RegisterDoneInitializingCallback(
      base::OnceCallback<void(bool)> init_done_callback);
  void MergeSyncDataForTesting(
      std::vector<std::vector<std::string>> app_vector);

  // internal helpers, exposed for testing.
  bool AppWasUsedRecently(const sync_pb::WebApkSpecifics* specifics) const;

  void PrepareRegistryUpdateFromSyncApps(
      const syncer::EntityChangeList& sync_changes,
      RegistryUpdateData* registry_update_from_sync) const;

  bool SyncDataContainsNewApps(
      const std::vector<std::unique_ptr<sync_pb::WebApkSpecifics>>&
          installed_apps,
      const syncer::EntityChangeList& sync_changes) const;

  void OnWebApkUsed(std::unique_ptr<sync_pb::WebApkSpecifics> app_specifics,
                    bool is_install);
  void OnWebApkUninstalled(const std::string& manifest_id);

  // Get list of apps that are available to restore.
  std::vector<WebApkRestoreData> GetRestorableAppsShortcutInfo() const;

  const WebApkProto* GetWebApkByAppId(webapps::AppId app_id) const;

  void SetClockForTesting(std::unique_ptr<base::Clock> clock);

  const Registry& GetRegistryForTesting() const;

  base::WeakPtr<syncer::DataTypeControllerDelegate>
  GetDataTypeControllerDelegate();

 private:
  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  enum class AddOrModifyType {
    // Note that the "Modification" items refer to _any_ modification of the
    // specifics proto - even just a timestamp change. This does not necessarily
    // imply that there is any change in the actual WebAPK.
    kNewInstallOnDeviceAndNewAddToSync = 0,
    kNewInstallOnDeviceAndModificationToSync = 1,
    kLaunchOnDeviceAndNewAddToSync = 2,
    kLaunchOnDeviceAndModificationToSync = 3,
    kMaxValue = kLaunchOnDeviceAndModificationToSync,
  };

  void ReportErrorToChangeProcessor(const syncer::ModelError& error);
  void OnDatabaseOpened(base::OnceClosure callback,
                        Registry registry,
                        std::unique_ptr<syncer::MetadataBatch> metadata_batch);
  void OnDataWritten(CommitCallback callback, bool success);

  // ApplyIncrementalSyncChangesToRegistry applies the changes in the app list
  // gathered in PrepareRegistryUpdateFromSyncApps() to the registry.
  void ApplyIncrementalSyncChangesToRegistry(
      std::unique_ptr<RegistryUpdateData> update_data);

  void AddOrModifyAppInSync(std::unique_ptr<WebApkProto> app, bool is_install);
  void DeleteAppsFromSync(const std::vector<webapps::AppId>& app_ids,
                          bool database_opened);

  void RecordSyncedWebApkAdditionHistogram(bool is_install,
                                           bool already_exists_in_sync) const;
  void RecordSyncedWebApkRemovalCountHistogram(int num_web_apks_removed) const;

  WebApkDatabase database_;
  Registry registry_;
  std::unique_ptr<base::Clock> clock_;
  std::unique_ptr<AbstractWebApkSpecificsFetcher> webapk_specifics_fetcher_;
  std::vector<base::OnceCallback<void(bool)>> init_done_callback_;

  base::WeakPtrFactory<WebApkSyncBridge> weak_ptr_factory_{this};
};

std::unique_ptr<syncer::EntityData> CreateSyncEntityData(
    const WebApkProto& app);
webapps::AppId ManifestIdStrToAppId(const std::string& manifest_id);

}  // namespace webapk

#endif  // CHROME_BROWSER_ANDROID_WEBAPK_WEBAPK_SYNC_BRIDGE_H_
