#pragma once

#include <paritypp/server.h>

#include <chrono>
#include <cstdint>
#include <list>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>

namespace mediadb {

// Disk-persisted block_store with the same byte-budget LRU eviction that
// paritypp::memory_block_store applies in-memory: once the configured
// max_bytes is exceeded, the least-recently-used group is evicted first.
//
// Wraps a paritypp::file_block_store for durability; eviction just tombstones
// the victim group there (file_block_store reclaims the space itself via its
// own auto-vacuum once dead space grows large).
//
// True access recency does not survive a restart. On construction the
// existing on-disk groups are re-indexed for byte accounting, and the LRU
// order starts from that scan — it reflects real usage again from then on.
class PersistentLruBlockStore : public paritypp::block_store {
public:
    // base_dir: directory for the underlying file_block_store
    // max_bytes: 0 = unbounded (no eviction, like memory_block_store)
    PersistentLruBlockStore(const std::string& base_dir, size_t max_bytes);

    bool store(uint64_t group_id, uint32_t block_index,
               const uint8_t* data, size_t len) override;
    bool fetch(uint64_t group_id, uint32_t block_index,
               std::vector<uint8_t>& out) override;
    bool remove_group(uint64_t group_id) override;
    std::vector<uint32_t> list_blocks(uint64_t group_id) override;
    uint32_t total_blocks() override;
    uint32_t total_groups() override;
    std::vector<uint64_t> list_groups() override;
    bool vacuum() override;

private:
    void touch(uint64_t group_id);
    void evict_lru();
    size_t group_bytes(uint64_t group_id) const;

    std::shared_ptr<paritypp::file_block_store> inner_;
    size_t max_bytes_;
    size_t used_bytes_ = 0;
    std::mutex mutex_;

    // group_id -> (block_index -> byte size), mirrors what's on disk
    std::unordered_map<uint64_t, std::unordered_map<uint32_t, size_t>> block_sizes_;
    // LRU order: front = most recently used, back = least recently used
    std::list<uint64_t> lru_;
    std::unordered_map<uint64_t, std::list<uint64_t>::iterator> lru_map_;
    // Last touch() time per group. A multi-stripe read or write touches a
    // group across several separate store()/fetch() calls, each releasing
    // mutex_ in between (every stripe is its own network round trip to a
    // remote node) — nothing else marks "still in use" across that gap.
    // evict_lru() refuses to evict anything touched within EVICT_GRACE, so
    // it can't delete blocks a concurrent multi-stripe reconstruction is
    // still relying on and silently truncate it.
    std::unordered_map<uint64_t, std::chrono::steady_clock::time_point> last_touch_;
};

} // namespace mediadb
