// 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 COMPONENTS_SERVICES_STORAGE_DOM_STORAGE_SQLITE_LOCAL_STORAGE_SQLITE_H_
#define COMPONENTS_SERVICES_STORAGE_DOM_STORAGE_SQLITE_LOCAL_STORAGE_SQLITE_H_

#include "base/trace_event/memory_allocator_dump_guid.h"
#include "base/trace_event/memory_dump_provider.h"
#include "components/services/storage/dom_storage/dom_storage_database.h"

namespace sql {
class Database;
class MetaTable;
}  // namespace sql

namespace storage {
class MapEntriesTable;

// Local storage creates two tables and one index in the database. The `maps`
// table contains rows like:
//
// -----------------------------------------------------------------------------
// | row_id | storage_key      | last_accessed  | last_modified  | total_size  |
// -----------------------------------------------------------------------------
// | 1      | https://a.test/  | <timestamp1>   | <timestamp2>   |   156 bytes |
// | 2      | https://b.test/  | <timestamp3>   | <timestamp4>   | 1,793 bytes |
// | 3      | https://c.test/  | <timestamp5>   | <timestamp6>   |     5 bytes |
//
// Each row is a map in the `map_entries_table_`.  The `row_id` column is an
// autogenerated map ID.  The `row_id` column also references the `map_id`
// column in the `map_entries_table_`, which is used to read and write a map's
// key/value pairs.
//
// The `maps_by_storage_key` index supports query by `storage_key` to find the
// map ID efficiently.  Each `storage_key` in `maps` must be unique, limiting
// each `storage_key` to a single persisted map.
class LocalStorageSqlite : public DomStorageDatabase,
                           private base::trace_event::MemoryDumpProvider {
 private:
  using PassKey = base::PassKey<DomStorageDatabaseFactory>;

 public:
  // Use `DomStorageDatabaseFactory::Open()` to construct a
  // base::SequenceBound<DomStorageDatabase>.
  explicit LocalStorageSqlite(PassKey);
  ~LocalStorageSqlite() override;

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

  // The `DomStorageDatabase` interface:
  DbStatus Open(const base::FilePath& database_path,
                const std::optional<base::trace_event::MemoryAllocatorDumpGuid>&
                    memory_dump_id) override;
  StatusOr<std::map<Key, Value>> ReadMapKeyValues(
      MapLocator map_locator) override;
  DbStatus UpdateMaps(std::vector<MapBatchUpdate> map_updates) override;
  DbStatus CloneMap(MapLocator source_map, MapLocator target_map) override;
  StatusOr<Metadata> ReadAllMetadata() override;
  DbStatus PutMetadata(Metadata metadata) override;
  DbStatus DeleteStorageKeysFromSession(
      std::string session_id,
      std::vector<blink::StorageKey> metadata_to_delete,
      std::vector<MapLocator> maps_to_delete) override;
  DbStatus DeleteSessions(std::vector<std::string> session_ids,
                          std::vector<MapLocator> maps_to_delete) override;
  DbStatus PurgeOrigins(std::set<url::Origin> origins) override;
  DbStatus CleanUpStaleData() override;
  void MakeAllCommitsFailForTesting() override;
  void SetDestructionCallbackForTesting(base::OnceClosure callback) override;
  DbStatus PutVersionForTesting(int64_t version) override;

 private:
  // Looks up the `map_id` (which is the `row_id` in the `maps` table) for the
  // given `storage_key`. Returns `std::nullopt` if no map exists for
  // `storage_key`.
  StatusOr<std::optional<int64_t>> FindMapId(
      const blink::StorageKey& storage_key);

  // Inserts or updates the metadata for the map identified by `storage_key` in
  // the `maps` table. If a row for `storage_key` already exists, only non-null
  // parameters are updated (existing values are preserved for null parameters).
  // If no row exists, a new row is inserted with all provided values.  The
  // caller must begin a database transaction before calling this function.
  DbStatus PutMapMetadata(const blink::StorageKey& storage_key,
                          std::optional<base::Time> last_accessed,
                          std::optional<base::Time> last_modified,
                          std::optional<base::ByteSize> total_size);

  // Deletes the metadata rows from the `maps` table for each storage key in
  // `metadata_to_delete`. The caller must begin a database transaction before
  // calling this function.
  DbStatus DeleteMapMetadata(
      const std::vector<blink::StorageKey>& metadata_to_delete);

  // base::trace_event::MemoryDumpProvider implementation:
  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
                    base::trace_event::ProcessMemoryDump* pmd) override;

  // `Open()` creates `database_`, `meta_table_` and `map_entries_table_`.
  std::unique_ptr<sql::Database> database_;
  std::unique_ptr<sql::MetaTable> meta_table_;
  std::unique_ptr<MapEntriesTable> map_entries_table_;

  std::optional<base::trace_event::MemoryAllocatorDumpGuid> memory_dump_id_;

  // Simulates I/O failure in `PutMetadata()` and `UpdateMaps()` by force
  // returning an IOError. Set to true by `MakeAllCommitsFailForTesting()`.
  bool should_fail_commits_for_testing_ = false;

  base::OnceClosure destruction_callback_for_testing_;
};

}  // namespace storage

#endif  // COMPONENTS_SERVICES_STORAGE_DOM_STORAGE_SQLITE_LOCAL_STORAGE_SQLITE_H_
