Feature: ACMS Hot Storage Tier (in-memory LRU cache)
  As an ACMS developer
  I want a hot storage tier backed by an in-memory LRU cache
  So that recently and frequently accessed context entries are served
  with minimal latency while capacity limits are enforced automatically

  # ---- Construction ----

  Scenario: Create HotStorageTier with no limits
    Given a HotStorageTier with no capacity limits
    Then the hot tier entry_count should be 0
    And the hot storage tier size_bytes should be 0
    And the hot tier hit_count should be 0
    And the hot tier miss_count should be 0

  Scenario: Create HotStorageTier with max_entries limit
    Given a HotStorageTier with max_entries 10
    Then the hot tier max_entries should be 10
    And the hot tier max_bytes should be None

  Scenario: Create HotStorageTier with max_bytes limit
    Given a HotStorageTier with max_bytes 1024
    Then the hot tier max_bytes should be 1024
    And the hot tier max_entries should be None

  Scenario: Create HotStorageTier with both limits
    Given a HotStorageTier with max_entries 5 and max_bytes 512
    Then the hot tier max_entries should be 5
    And the hot tier max_bytes should be 512

  Scenario: HotStorageTier rejects max_entries of zero
    Then creating a HotStorageTier with max_entries 0 should raise ValueError

  Scenario: HotStorageTier rejects negative max_entries
    Then creating a HotStorageTier with max_entries -1 should raise ValueError

  Scenario: HotStorageTier rejects max_bytes of zero
    Then creating a HotStorageTier with max_bytes 0 should raise ValueError

  Scenario: HotStorageTier rejects negative max_bytes
    Then creating a HotStorageTier with max_bytes -1 should raise ValueError

  # ---- Basic put/get ----

  Scenario: Put and get a single entry
    Given a HotStorageTier with no capacity limits
    When I put entry "key1" with content "hello world"
    Then getting "key1" from the hot tier should return "hello world"

  Scenario: Get returns None for missing entry
    Given a HotStorageTier with no capacity limits
    Then getting "missing" from the hot tier should return None

  Scenario: Put updates existing entry
    Given a HotStorageTier with no capacity limits
    When I put entry "key1" with content "original"
    And I put entry "key1" with content "updated"
    Then getting "key1" from the hot tier should return "updated"
    And the hot tier entry_count should be 1

  Scenario: Put rejects empty entry_id
    Given a HotStorageTier with no capacity limits
    Then putting an entry with empty id should raise ValueError

  # ---- Metrics ----

  Scenario: Hit count increments on successful get
    Given a HotStorageTier with no capacity limits
    When I put entry "k1" with content "data"
    And I get "k1" from the hot tier
    Then the hot tier hit_count should be 1
    And the hot tier miss_count should be 0

  Scenario: Miss count increments on failed get
    Given a HotStorageTier with no capacity limits
    When I get "nonexistent" from the hot tier
    Then the hot tier hit_count should be 0
    And the hot tier miss_count should be 1

  Scenario: Hit and miss counts accumulate independently
    Given a HotStorageTier with no capacity limits
    When I put entry "k1" with content "data"
    And I get "k1" from the hot tier
    And I get "k1" from the hot tier
    And I get "missing" from the hot tier
    Then the hot tier hit_count should be 2
    And the hot tier miss_count should be 1

  Scenario: entry_count reflects current cache size
    Given a HotStorageTier with no capacity limits
    When I put entry "a" with content "aaa"
    And I put entry "b" with content "bbb"
    And I put entry "c" with content "ccc"
    Then the hot tier entry_count should be 3

  Scenario: size_bytes reflects UTF-8 encoded content size
    Given a HotStorageTier with no capacity limits
    When I put entry "k1" with content "hello"
    Then the hot storage tier size_bytes should be 5

  Scenario: size_bytes updates when entry is replaced
    Given a HotStorageTier with no capacity limits
    When I put entry "k1" with content "hi"
    And I put entry "k1" with content "hello world"
    Then the hot storage tier size_bytes should be 11

  # ---- LRU eviction: max_entries ----

  Scenario: LRU eviction triggers when max_entries is exceeded
    Given a HotStorageTier with max_entries 3
    When I put entry "e1" with content "first"
    And I put entry "e2" with content "second"
    And I put entry "e3" with content "third"
    And I put entry "e4" with content "fourth"
    Then the hot tier entry_count should be 3
    And getting "e1" from the hot tier should return None
    And getting "e4" from the hot tier should return "fourth"

  Scenario: LRU order is updated on get
    Given a HotStorageTier with max_entries 2
    When I put entry "a" with content "aaa"
    And I put entry "b" with content "bbb"
    And I get "a" from the hot tier
    And I put entry "c" with content "ccc"
    Then getting "a" from the hot tier should return "aaa"
    And getting "b" from the hot tier should return None
    And getting "c" from the hot tier should return "ccc"

  Scenario: LRU order is updated on put (update existing)
    Given a HotStorageTier with max_entries 2
    When I put entry "a" with content "aaa"
    And I put entry "b" with content "bbb"
    And I put entry "a" with content "aaa-updated"
    And I put entry "c" with content "ccc"
    Then getting "a" from the hot tier should return "aaa-updated"
    And getting "b" from the hot tier should return None
    And getting "c" from the hot tier should return "ccc"

  # ---- LRU eviction: max_bytes ----

  Scenario: LRU eviction triggers when max_bytes is exceeded
    Given a HotStorageTier with max_bytes 10
    When I put entry "k1" with content "12345"
    And I put entry "k2" with content "67890"
    And I put entry "k3" with content "abcde"
    Then the hot storage tier size_bytes should be at most 10
    And getting "k1" from the hot tier should return None

  Scenario: Single entry larger than max_bytes is evicted immediately
    Given a HotStorageTier with max_bytes 3
    When I put entry "big" with content "hello"
    Then the hot tier entry_count should be 0
    And the hot storage tier size_bytes should be 0

  # ---- Eviction callback (warm-tier demotion) ----

  Scenario: on_evict callback is called with evicted entry id
    Given a HotStorageTier with max_entries 2 and an eviction recorder
    When I put entry "x1" with content "content1"
    And I put entry "x2" with content "content2"
    And I put entry "x3" with content "content3"
    Then the eviction recorder should have recorded entry id "x1"

  Scenario: on_evict callback receives correct content for evicted entry
    Given a HotStorageTier with max_entries 1 and an eviction recorder
    When I put entry "first" with content "first-content"
    And I put entry "second" with content "second-content"
    Then the eviction recorder should have recorded entry "first" with content "first-content"

  Scenario: on_evict callback error does not interrupt cache operation
    Given a HotStorageTier with max_entries 1 and a failing eviction callback
    When I put entry "a" with content "aaa"
    And I put entry "b" with content "bbb"
    Then the hot tier entry_count should be 1
    And getting "b" from the hot tier should return "bbb"

  Scenario: on_evict is not called when no eviction occurs
    Given a HotStorageTier with max_entries 5 and an eviction recorder
    When I put entry "only" with content "data"
    Then the eviction recorder should have recorded 0 evictions

  # ---- Remove ----

  Scenario: Remove an existing entry
    Given a HotStorageTier with no capacity limits
    When I put entry "r1" with content "remove-me"
    And I remove "r1" from the hot tier
    Then getting "r1" from the hot tier should return None
    And the hot tier entry_count should be 0

  Scenario: Remove returns the content of the removed entry
    Given a HotStorageTier with no capacity limits
    When I put entry "r2" with content "my-content"
    And I remove "r2" from the hot tier
    Then removing "r2" from the hot tier should return "my-content"

  Scenario: Remove returns None for missing entry
    Given a HotStorageTier with no capacity limits
    When I remove "nonexistent" from the hot tier
    Then removing "nonexistent" from the hot tier should return None

  Scenario: Remove updates size_bytes correctly
    Given a HotStorageTier with no capacity limits
    When I put entry "s1" with content "hello"
    And I remove "s1" from the hot tier
    Then the hot storage tier size_bytes should be 0

  # ---- Clear ----

  Scenario: Clear removes all entries
    Given a HotStorageTier with no capacity limits
    When I put entry "c1" with content "aaa"
    And I put entry "c2" with content "bbb"
    And I clear the hot tier
    Then the hot tier entry_count should be 0
    And the hot storage tier size_bytes should be 0

  Scenario: Clear does not reset hit/miss counters
    Given a HotStorageTier with no capacity limits
    When I put entry "c1" with content "aaa"
    And I get "c1" from the hot tier
    And I clear the hot tier
    Then the hot tier hit_count should be 1

  # ---- Thread safety ----

  Scenario: Concurrent puts from multiple threads do not corrupt the cache
    Given a HotStorageTier with max_entries 50
    When 10 threads concurrently put 5 entries each into the hot tier
    Then no exception should have been raised during concurrent puts
    And the hot tier entry_count should be at most 50

  Scenario: Concurrent gets and puts do not raise RuntimeError
    Given a HotStorageTier with no capacity limits
    When 5 threads concurrently put entries and 5 threads concurrently get entries
    Then no exception should have been raised during concurrent gets and puts

  Scenario: Concurrent evictions do not corrupt size_bytes
    Given a HotStorageTier with max_bytes 100
    When 8 threads concurrently put large entries into the hot tier
    Then no exception should have been raised during concurrent evictions
    And the hot storage tier size_bytes should be at most 100
