// 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_BACKEND_STORAGE_H_
#define COMPONENTS_PERSISTENT_CACHE_BACKEND_STORAGE_H_

#include <stdint.h>

#include <memory>
#include <optional>

#include "base/component_export.h"
#include "base/files/file_path.h"
#include "base/types/expected.h"
#include "components/persistent_cache/transaction_error.h"

namespace persistent_cache {

class Backend;
enum class BackendType;
enum class Client;
class PersistentCache;
struct PendingBackend;

// Manages the storage of files for backends within a directory. Only one
// instance per process is permitted to operate on a given directory at a time.
class COMPONENT_EXPORT(PERSISTENT_CACHE) BackendStorage {
 public:
  // A delegate interface to be implemented by each concrete type of backend.
  // The delegate is responsible for managing backends in a directory identified
  // by their base names.
  class Delegate {
   public:
    virtual ~Delegate() = default;

    // Returns a new pending read-write backend named `base_name` within
    // `directory`. If `single_connection` is true, the returned instance may be
    // used to open only one `PersistentCache` -- connections to that cache
    // cannot be shared. If `journal_mode_wal` (which only applies to the SQLite
    // backend) is true, the database will use write-ahead log journaling.
    // Returns no value in case of error (e.g., if the backend's files could not
    // be opened or created).
    virtual base::expected<PendingBackend, TransactionError> MakePendingBackend(
        Client client,
        const base::FilePath& directory,
        const base::FilePath& base_name,
        bool single_connection,
        bool journal_mode_wal) = 0;

    // Returns a new read-write backend named `base_name` within `directory`. If
    // `single_connection` is true, the returned backend may be used by only one
    // `PersistentCache` instance -- connections to it cannot be shared for use
    // by other instances. If `journal_mode_wal` (which only applies to the
    // SQLite backend) is true, the database will use write-ahead log
    // journaling. Returns an error code on failure (e.g., if the backend's
    // files could not be opened or created, or the backend's storage is
    // corrupt). See persistent_cache.h for details regarding error handling.
    virtual base::expected<std::unique_ptr<Backend>, TransactionError>
    MakeBackend(Client client,
                const base::FilePath& directory,
                const base::FilePath& base_name,
                bool single_connection,
                bool journal_mode_wal) = 0;

    // Returns a pending backend for a read-only connection to the backend named
    // `base_name` within `directory`. This allows another party to bind to an
    // existing backend.
    virtual std::optional<PendingBackend> ShareReadOnlyConnection(
        const base::FilePath& directory,
        const base::FilePath& base_name,
        const Backend& backend) = 0;

    // Returns a pending backend for a read-write connection to the backend
    // named `base_name` within `directory`. This allows another party to bind
    // to an existing backend.
    virtual std::optional<PendingBackend> ShareReadWriteConnection(
        const base::FilePath& directory,
        const base::FilePath& base_name,
        const Backend& backend) = 0;

    // Returns the basename of `file` if it names a file managed by the backend,
    // or an empty path otherwise.
    virtual base::FilePath GetBaseName(const base::FilePath& file) = 0;

    // Deletes all files corresponding to the backend named `base_name` in
    // `directory`. Returns the total size, in bytes, of all files deleted.
    virtual int64_t DeleteFiles(Client client,
                                const base::FilePath& directory,
                                const base::FilePath& base_name) = 0;

   protected:
    Delegate() = default;
  };

  // Constructs an instance that will use the given backend type for file
  // management within `directory`. Creates `directory` if it does not already
  // exist.
  BackendStorage(Client client,
                 BackendType backend_type,
                 base::FilePath directory);

  // Constructs an instance that will use `delegate` for file management within
  // `directory`. Creates `directory` if it does not already exist.
  BackendStorage(Client client,
                 std::unique_ptr<Delegate> delegate,
                 base::FilePath directory);
  BackendStorage(const BackendStorage&) = delete;
  BackendStorage& operator=(const BackendStorage&) = delete;
  ~BackendStorage();

  Client client() const { return client_; }

  // Returns the directory managed by the instance.
  const base::FilePath& directory() const { return directory_; }

  // Returns a new pending read-write backend named `base_name` within the
  // instance's directory. If `single_connection` is true, the returned instance
  // may be used to open only one `PersistentCache` -- connections to that cache
  // cannot be shared. If `journal_mode_wal` (which only applies to the SQLite
  // backend) is true, the database will use write-ahead log journaling. Returns
  // `TransactionError::kTransient` in case of an error for which a retry may
  // succeed (e.g., `single_connection` is true and the main database file is
  // in-use).
  base::expected<PendingBackend, TransactionError> MakePendingBackend(
      const base::FilePath& base_name,
      bool single_connection,
      bool journal_mode_wal);

  // Returns a new read-write backend named `base_name` within the instance's
  // directory. If `single_connection` is true, the returned backend may be used
  // by only one `PersistentCache` instance -- connections to it cannot be
  // shared for use by other instances. If `journal_mode_wal` (which only
  // applies to the SQLite backend) is true, the database will use write-ahead
  // log journaling. Returns an error code on failure (e.g., if the backend's
  // files could not be opened or created, or the backend's storage is corrupt).
  // See persistent_cache.h for details regarding error handling.
  base::expected<std::unique_ptr<Backend>, TransactionError> MakeBackend(
      const base::FilePath& base_name,
      bool single_connection,
      bool journal_mode_wal);

  // Returns a pending backend for a read-only connection to the backend named
  // `base_name` within the instance's directory. This allows another party to
  // bind to an existing backend.
  std::optional<PendingBackend> ShareReadOnlyConnection(
      const base::FilePath& base_name,
      const PersistentCache& cache);

  // Returns a pending backend for a read-write connection to the backend named
  // `base_name` within the instance's directory. This allows another party to
  // bind to an existing backend.
  std::optional<PendingBackend> ShareReadWriteConnection(
      const base::FilePath& base_name,
      const PersistentCache& cache);

  // Deletes all files in the instance's directory. Any outstanding backend
  // instances will continue to operate on the deleted files, and no new
  // backends using them should be created. An attempt to do so may fail (and
  // likely will on Windows). The caller should ensure that all outstanding
  // backends are destroyed before creating new ones in the managed directory.
  void DeleteAllFiles();

  // Delete files associated with `base_name` within the instance's directory.
  // The conditions apply as in `DeleteAllFiles`.
  void DeleteFiles(const base::FilePath& base_name);

  struct FootprintReductionResult {
    int64_t current_footprint;
    int64_t number_of_bytes_deleted;
  };

  // Deletes backend files from oldest-to-newest to bring the total disk usage
  // within the instance's directory down to `target_footprint`. Returns the
  // current footprint and the number of bytes deleted in the operation, if any.
  FootprintReductionResult BringDownTotalFootprintOfFiles(
      int64_t target_footprint);

 private:
  const Client client_;

  // The delegate used to create/operate on backends.
  const std::unique_ptr<Delegate> delegate_;

  // The directory in which backends reside.
  const base::FilePath directory_;

  // True if the instance has succeeded in creating its directory.
  bool is_valid_ = false;
};

}  // namespace persistent_cache

#endif  // COMPONENTS_PERSISTENT_CACHE_BACKEND_STORAGE_H_
