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

#ifndef NET_DISK_CACHE_SQL_SQL_SHARED_CACHE_MANAGER_H_
#define NET_DISK_CACHE_SQL_SQL_SHARED_CACHE_MANAGER_H_

#include <map>
#include <memory>
#include <utility>
#include <vector>

#include "base/containers/flat_map.h"
#include "base/containers/queue.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/files/file_path.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ref.h"
#include "base/memory/scoped_refptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/threading/sequence_bound.h"
#include "net/base/net_export.h"
#include "net/base/network_isolation_key.h"
#include "net/disk_cache/sql/sql_read_cache_memory_monitor.h"
#include "net/disk_cache/sql/sql_shared_cache.h"
#include "net/disk_cache/sql/sql_shared_cache_handle.h"
#include "net/disk_cache/sql/sql_shared_cache_index_database.h"
#include "net/disk_cache/sql/sql_tracked_sequence_bound.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"

namespace disk_cache {

class BackendCleanupTracker;
class SharedCacheClientRemote;
class SqlPersistentStore;

// Manages the creation, lookup, and lifecycle of `SqlSharedCache` instances.
//
// Maintains mapping between NetworkIsolationKey string representation / DbId
// and `SqlSharedCache` objects. Handles serialized database operations for
// initializing index databases and retrieving or creating shared caches.
class NET_EXPORT_PRIVATE SqlSharedCacheManager {
 public:
  using InitCallback = base::OnceCallback<void(
      base::expected<void, SqlSharedCacheIndexDatabase::Error>)>;

  SqlSharedCacheManager(
      SqlPersistentStore& store,
      const base::FilePath& path,
      scoped_refptr<SqlReadCacheMemoryMonitor> read_cache_memory_monitor,
      scoped_refptr<BackendCleanupTracker> cleanup_tracker);
  ~SqlSharedCacheManager();

  // Asynchronously initializes the index database.
  void Init(InitCallback callback);

  // Asynchronously retrieves a `SqlSharedCacheHandle` associated with the given
  // `shared_cache_db_id`. Calls `callback` with a handle (or nullptr if lookup
  // fails/error occurs).
  void GetCacheByDbId(
      SqlSharedCacheDbId shared_cache_db_id,
      base::OnceCallback<void(scoped_refptr<SqlSharedCacheHandle>)> callback);

  // Asynchronously retrieves or creates a `SqlSharedCacheHandle` for the given
  // `nik`. If `require_shared_cache_db_id` is true, an entry in the index
  // database will be created/resolved.
  void GetCacheByNik(
      const net::NetworkIsolationKey& nik,
      bool require_shared_cache_db_id,
      base::OnceCallback<void(scoped_refptr<SqlSharedCacheHandle>)> callback);

  // Asynchronously deletes the shared cache resources specified by `resources`.
  // The resources are grouped by their database ID and deleted from their
  // corresponding isolated databases. Invokes `callback` upon completion.
  void DeleteResources(std::vector<SqlSharedCacheResourceId> resources,
                       base::OnceClosure callback);

  // Asynchronously copies eligible entries into their corresponding isolated
  // shared cache databases grouped by NetworkIsolationKey. Unprocessed entries
  // are returned via `callback`.
  void ProcessSharedCacheEligibleEntries(
      std::map<net::NetworkIsolationKey,
               base::queue<SqlPersistentStore::SharedCacheEligibleEntry>>
          entries,
      scoped_refptr<base::RefCountedData<std::atomic_bool>> abort_flag,
      base::OnceCallback<void(
          std::vector<SqlPersistentStore::SharedCacheEligibleEntry>)> callback,
      base::RepeatingCallback<void(const CacheEntryKey&)>
          on_entry_copied_callback = {});

  // Registers a remote shared cache client for the given NetworkIsolationKey.
  // The client will be notified with a read-only database connection once
  // available.
  void RegisterClient(const net::NetworkIsolationKey& network_isolation_key,
                      std::unique_ptr<SharedCacheClientRemote> client);

  // Sets a flag to simulate index database operation failures for testing.
  void SetSimulateDbFailureForTesting(bool fail);

  size_t GetSharedCachesSizeForTest() const { return shared_caches_.size(); }

  size_t GetSharedCachesByDbIdSizeForTest() const {
    return shared_caches_by_shared_cache_db_id_.size();
  }

  size_t GetSharedCachesByNikSizeForTest() const {
    return shared_caches_by_nik_string_.size();
  }

 private:
  friend class SqlSharedCacheManagerTest;

  // Handle used to signal completion of a serialized database operation.
  // When destroyed, `FinishDbOperation()` is invoked to run the next queued
  // operation.
  using DbOperationHandle = base::ScopedClosureRunner;

  void PostDbOperation(base::OnceCallback<void(DbOperationHandle)> operation);
  void MaybeRunNextDbOperation();
  void FinishDbOperation();

  void OnSqlSharedCacheUnreferenced(SqlSharedCache& cache);
  void DoDeleteUnreferencedSqlSharedCache(
      const std::string& nik_string,
      DbOperationHandle db_operation_handle);

  void DoGetCacheByNik(
      net::NetworkIsolationKey nik,
      bool require_shared_cache_db_id,
      base::OnceCallback<void(scoped_refptr<SqlSharedCacheHandle>)> callback,
      DbOperationHandle db_operation_handle);
  void OnGetSharedDbIdForNik(
      net::NetworkIsolationKey nik,
      base::OnceCallback<void(scoped_refptr<SqlSharedCacheHandle>)> callback,
      DbOperationHandle db_operation_handle,
      base::expected<SqlSharedCacheDbId, SqlSharedCacheIndexDatabase::Error>
          result);
  scoped_refptr<SqlSharedCacheHandle> RegisterNewSqlSharedCache(
      const std::string& nik_str,
      std::optional<SqlSharedCacheDbId> shared_cache_db_id,
      DbOperationHandle db_operation_handle);

  void DoGetCacheByDbId(
      SqlSharedCacheDbId shared_cache_db_id,
      base::OnceCallback<void(scoped_refptr<SqlSharedCacheHandle>)> callback,
      DbOperationHandle db_operation_handle);
  void OnGetNikStringForDbId(
      SqlSharedCacheDbId shared_cache_db_id,
      base::OnceCallback<void(scoped_refptr<SqlSharedCacheHandle>)> callback,
      DbOperationHandle db_operation_handle,
      base::expected<std::string, SqlSharedCacheIndexDatabase::Error> result);

  void DeleteNextResourceGroup(
      absl::flat_hash_map<SqlSharedCacheDbId, std::vector<SqlSharedCacheRowId>>
          grouped_resources,
      base::OnceClosure callback,
      DbOperationHandle db_operation_handle);

  void DoProcessSharedCacheEligibleEntries(
      std::map<net::NetworkIsolationKey,
               base::queue<SqlPersistentStore::SharedCacheEligibleEntry>>
          entries,
      scoped_refptr<base::RefCountedData<std::atomic_bool>> abort_flag,
      base::OnceCallback<void(
          std::vector<SqlPersistentStore::SharedCacheEligibleEntry>)> callback,
      base::RepeatingCallback<void(const CacheEntryKey&)>
          on_entry_copied_callback,
      DbOperationHandle db_operation_handle);

  void ProcessNextNikGroup(
      base::queue<base::queue<SqlPersistentStore::SharedCacheEligibleEntry>>
          groups,
      scoped_refptr<base::RefCountedData<std::atomic_bool>> abort_flag,
      std::vector<SqlPersistentStore::SharedCacheEligibleEntry> all_unprocessed,
      base::OnceCallback<void(
          std::vector<SqlPersistentStore::SharedCacheEligibleEntry>)> callback,
      base::RepeatingCallback<void(const CacheEntryKey&)>
          on_entry_copied_callback);
  void OnGetSharedCacheForProcess(
      net::NetworkIsolationKey current_nik,
      base::queue<base::queue<SqlPersistentStore::SharedCacheEligibleEntry>>
          groups,
      scoped_refptr<base::RefCountedData<std::atomic_bool>> abort_flag,
      std::vector<SqlPersistentStore::SharedCacheEligibleEntry> all_unprocessed,
      base::OnceCallback<void(
          std::vector<SqlPersistentStore::SharedCacheEligibleEntry>)> callback,
      base::RepeatingCallback<void(const CacheEntryKey&)>
          on_entry_copied_callback,
      scoped_refptr<SqlSharedCacheHandle> handle);
  void OnProcessEntryCompleted(
      scoped_refptr<SqlSharedCacheHandle> handle,
      base::queue<base::queue<SqlPersistentStore::SharedCacheEligibleEntry>>
          groups,
      scoped_refptr<base::RefCountedData<std::atomic_bool>> abort_flag,
      std::vector<SqlPersistentStore::SharedCacheEligibleEntry> all_unprocessed,
      base::OnceCallback<void(
          std::vector<SqlPersistentStore::SharedCacheEligibleEntry>)> callback,
      base::RepeatingCallback<void(const CacheEntryKey&)>
          on_entry_copied_callback,
      base::queue<SqlPersistentStore::SharedCacheEligibleEntry> results);

  const raw_ref<SqlPersistentStore> store_;
  const base::FilePath directory_;
  scoped_refptr<base::SequencedTaskRunner> db_task_runner_;
  SqlTrackedSequenceBound<SqlSharedCacheIndexDatabase> index_database_;
  scoped_refptr<SqlReadCacheMemoryMonitor> read_cache_memory_monitor_;
  scoped_refptr<BackendCleanupTracker> cleanup_tracker_;

  base::queue<base::OnceCallback<void(DbOperationHandle)>>
      pending_db_operations_;
  bool db_operation_in_progress_ = true;

  base::flat_set<std::unique_ptr<SqlSharedCache>, base::UniquePtrComparator>
      shared_caches_;
  absl::flat_hash_map<SqlSharedCacheDbId, raw_ptr<SqlSharedCache>>
      shared_caches_by_shared_cache_db_id_;
  absl::flat_hash_map<std::string, raw_ptr<SqlSharedCache>>
      shared_caches_by_nik_string_;

  base::WeakPtrFactory<SqlSharedCacheManager> weak_factory_{this};
};

}  // namespace disk_cache

#endif  // NET_DISK_CACHE_SQL_SQL_SHARED_CACHE_MANAGER_H_
