// 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_ENTRY_DB_HANDLE_H_
#define NET_DISK_CACHE_SQL_ENTRY_DB_HANDLE_H_

#include <optional>
#include <variant>

#include "base/memory/ref_counted.h"
#include "base/sequence_checker.h"
#include "net/base/net_export.h"
#include "net/disk_cache/sql/sql_persistent_store.h"
#include "net/disk_cache/sql/sql_shared_cache_blob_handle.h"
#include "net/disk_cache/sql/sql_shared_cache_handle.h"

namespace disk_cache {

// This class holds the resource ID (ResId) of a SQL cache entry.
// For speculatively created entries, the ResId might not be available
// initially. This class allows setting the ResId or an Error later.
// It is ref-counted so it can be shared between SqlEntryImpl and
// SqlBackendImpl operations.
class NET_EXPORT_PRIVATE EntryDbHandle
    : public base::RefCounted<EntryDbHandle> {
 public:
  // SqlBackendImpl::OpenOrCreateEntry and CreateEntry perform speculative
  // creation and synchronously return a SqlEntryImpl holding an EntryDbHandle
  // in the kInitial state when the DB's in-memory index indicates that no
  // entry with the corresponding key exists in the DB.
  //
  // The state changes to kCreating when writing to the DB becomes necessary
  // (e.g., when the write buffering limit is exceeded), and then to kCreated
  // upon completion. If the DB write fails, the state becomes kErrorOccurred.
  //
  // If an existing entry is opened, or if speculative creation is skipped
  // (e.g., because the in-memory index is not yet loaded), a SqlEntryImpl
  // holding an EntryDbHandle in the kCreated state is created after the DB
  // read or write operation completes.
  enum class State {
    kInitial = 0,
    kCreating,
    kCreated,
    kErrorOccurred,
  };

  EntryDbHandle();
  explicit EntryDbHandle(SqlPersistentStore::ResId res_id);

  void MarkAsCreating();
  void MarkAsCreated(SqlPersistentStore::ResId res_id);
  void MarkAsErrorOccurred(SqlPersistentStore::Error error);

  void set_shared_cache_resource_id(
      std::optional<SqlSharedCacheResourceId> shared_cache_resource_id);
  const std::optional<SqlSharedCacheResourceId>& shared_cache_resource_id()
      const;

  // For entries whose body has been moved to the shared cache,
  // `shared_cache_resource_id` is always set.
  // However, while `shared_cache_handle` and `shared_cache_blob_handle` are
  // set for explicitly opened entries, they are NOT set for entries retrieved
  // via entry iteration. This is a deliberate optimization to keep iteration
  // fast.
  //
  // `EntryDbHandle` holds a `shared_cache_handle` to open the corresponding
  // shared cache DB as early as possible and keep it open for the lifetime of
  // the entry in memory.
  //
  // Additionally, `EntryDbHandle` holds a `shared_cache_blob_handle` to keep
  // the underlying `sql::StreamingBlobHandle` alive within the
  // `SqlSharedCacheIsolatedDatabase`, avoiding the overhead of repeated DB
  // lookups.
  void SetSharedCacheHandle(
      scoped_refptr<SqlSharedCacheHandle> shared_cache_handle);

  void SetSharedCacheBlobHandle(
      scoped_refptr<SqlSharedCacheBlobHandle> shared_cache_blob_handle);

  std::optional<SqlPersistentStore::ResId> GetResId() const;
  std::optional<SqlPersistentStore::Error> GetError() const;

  bool IsInitialState() const { return state_ == State::kInitial; }
  bool IsCreatingState() const { return state_ == State::kCreating; }

  // Returns true if the ResId has been set or an error has occurred.
  bool IsFinished() const;

  // Marks the entry as doomed. This is called by the backend when an
  // active entry is doomed.
  void MarkAsDoomed();
  bool doomed() const;

 private:
  friend class base::RefCounted<EntryDbHandle>;
  ~EntryDbHandle();

  std::optional<
      std::variant<SqlPersistentStore::ResId, SqlPersistentStore::Error>>
      data_;

  State state_;

  std::optional<SqlSharedCacheResourceId> shared_cache_resource_id_;

  scoped_refptr<SqlSharedCacheHandle> shared_cache_handle_;
  scoped_refptr<SqlSharedCacheBlobHandle> shared_cache_blob_handle_;

  // True if this entry has been marked for deletion.
  bool doomed_ = false;

  SEQUENCE_CHECKER(sequence_checker_);
};

}  // namespace disk_cache

#endif  // NET_DISK_CACHE_SQL_ENTRY_DB_HANDLE_H_
