// 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_PERSISTENT_CACHE_SQLITE_SQLITE_BACKEND_IMPL_H_
#define COMPONENTS_PERSISTENT_CACHE_SQLITE_SQLITE_BACKEND_IMPL_H_

#include <stdint.h>

#include <memory>
#include <optional>

#include "base/component_export.h"
#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/gtest_prod_util.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "base/types/expected.h"
#include "base/types/pass_key.h"
#include "components/persistent_cache/backend.h"
#include "components/persistent_cache/pending_backend.h"
#include "components/persistent_cache/transaction_error.h"
#include "components/sqlite_vfs/sqlite_database_vfs_file_set.h"
#include "components/sqlite_vfs/sqlite_sandboxed_vfs.h"
#include "sql/database.h"

namespace persistent_cache {

enum class Client;

class COMPONENT_EXPORT(PERSISTENT_CACHE) SqliteBackendImpl : public Backend {
 public:
  // This value is to be incremented when the database schema used by this class
  // evolves in a non-backwards compatible way. When this number changes all
  // existing databases will be cleared on the first call to `Bind()`.
  static constexpr int kCurrentUserVersion = 3;

  static base::expected<std::unique_ptr<Backend>, TransactionError> Bind(
      PendingBackend pending_backend,
      Client client);

  using Passkey = base::PassKey<SqliteBackendImpl>;
  ~SqliteBackendImpl() override;

  SqliteBackendImpl(const SqliteBackendImpl&) = delete;
  SqliteBackendImpl(SqliteBackendImpl&&) = delete;
  SqliteBackendImpl& operator=(const SqliteBackendImpl&) = delete;
  SqliteBackendImpl& operator=(SqliteBackendImpl&&) = delete;

  // `Backend`:
  [[nodiscard]] base::expected<std::optional<EntryMetadata>, TransactionError>
  Find(base::span<const uint8_t> key, BufferProvider buffer_provider) override;
  base::expected<void, TransactionError> Insert(
      base::span<const uint8_t> key,
      base::span<const uint8_t> content,
      EntryMetadata metadata) override;
  BackendType GetType() const override;
  bool IsReadOnly() const override;
  LockState Abandon() override;

  const sqlite_vfs::SqliteVfsFileSet& file_set() const { return vfs_file_set_; }

  // Executes `statement` on the underlying database. Returns a SQLite result
  // code in case of error.
  base::expected<void, int> ExecuteStatementForTesting(
      base::cstring_view statement);

 private:
  FRIEND_TEST_ALL_PREFIXES(PersistentCacheTest, RecoveryFromTransientError);

  explicit SqliteBackendImpl(sqlite_vfs::SqliteVfsFileSet vfs_file_set,
                             Client client);
  [[nodiscard]] base::expected<void, TransactionError> Initialize();

  // Returns a SQLite error code in case of failure.
  base::expected<void, int> InitializeImpl() EXCLUSIVE_LOCKS_REQUIRED(lock_);

  // Returns a SQLite error code in case of failure.
  base::expected<std::optional<EntryMetadata>, int> FindImpl(
      base::span<const uint8_t> key,
      BufferProvider buffer_provider) EXCLUSIVE_LOCKS_REQUIRED(lock_);

  // Inserts `content` and `metadata` into storage under `key`. Returns a SQLite
  // extended result code in case of error.
  base::expected<void, int> InsertImpl(base::span<const uint8_t> key,
                                       base::span<const uint8_t> content,
                                       int64_t input_signature)
      EXCLUSIVE_LOCKS_REQUIRED(lock_);

  // Returns the `TransactionError` corresponding to a SQLite extended result
  // code.
  static TransactionError TranslateError(int error_code);

  const base::FilePath database_path_;

  // The set of of `SanboxedFiles` accessible by this backend. This class owns
  // the `SandboxedFiles`.
  sqlite_vfs::SqliteVfsFileSet vfs_file_set_;

  // Owns the registration / unregistration of the `SanboxedFiles` own by this
  // backend to the `SqliteSandboxedVfsDelegate`. Must be defined after
  // `vfs_file_set_` to ensures unregistration occurs before the `vfs_file_set_`
  // is released.
  sqlite_vfs::SqliteSandboxedVfsDelegate::UnregisterRunner unregister_runner_;

  // Defined after `unregister_runner_` to ensure that files remain available
  // through the VFS throughout the database's lifetime.
  std::optional<sql::Database> db_ GUARDED_BY(lock_);

  base::Lock lock_;
};

}  // namespace persistent_cache

#endif  // COMPONENTS_PERSISTENT_CACHE_SQLITE_SQLITE_BACKEND_IMPL_H_
