<?php

declare(strict_types=1);

use App\Framework\Cache\CacheItem;
use App\Framework\Cache\CacheKey;
use App\Framework\Cache\Driver\InMemoryCache;
use App\Framework\Core\ValueObjects\Duration;

beforeEach(function () {
    $this->cache = new InMemoryCache();
});

test('get returns miss for non-existent key', function () {
    $key = CacheKey::fromString('non-existent');
    $result = $this->cache->get($key);

    expect($result->isHit)->toBeFalse()
        ->and($result->value)->toBeNull();
});

test('set and get stores and retrieves value', function () {
    $key = CacheKey::fromString('test-key');
    $value = 'test-value';

    $result = $this->cache->set(CacheItem::forSet($key, $value));

    expect($result)->toBeTrue();

    $cacheResult = $this->cache->get($key);

    expect($cacheResult->isHit)->toBeTrue()
        ->and($cacheResult->value)->toBe($value);
});

test('has returns correct existence status', function () {
    $key = CacheKey::fromString('test-key');

    $hasResult = $this->cache->has($key);
    expect($hasResult['test-key'])->toBeFalse();

    $this->cache->set(CacheItem::forSet($key, 'value'));

    $hasResult = $this->cache->has($key);
    expect($hasResult['test-key'])->toBeTrue();
});

test('forget removes item from cache', function () {
    $key = CacheKey::fromString('test-key');
    $this->cache->set(CacheItem::forSet($key, 'value'));

    $hasResult = $this->cache->has($key);
    expect($hasResult['test-key'])->toBeTrue();

    $result = $this->cache->forget($key);

    expect($result)->toBeTrue();

    $hasResult = $this->cache->has($key);
    expect($hasResult['test-key'])->toBeFalse();
});

test('clear removes all items from cache', function () {
    $key1 = CacheKey::fromString('key1');
    $key2 = CacheKey::fromString('key2');

    $this->cache->set(CacheItem::forSet($key1, 'value1'));
    $this->cache->set(CacheItem::forSet($key2, 'value2'));

    $hasResult = $this->cache->has($key1, $key2);
    expect($hasResult['key1'])->toBeTrue();
    expect($hasResult['key2'])->toBeTrue();

    $result = $this->cache->clear();

    expect($result)->toBeTrue();

    $hasResult = $this->cache->has($key1, $key2);
    expect($hasResult['key1'])->toBeFalse();
    expect($hasResult['key2'])->toBeFalse();
});

test('set with ttl parameter still stores value', function () {
    $key = CacheKey::fromString('test-key');
    $value = 'test-value';
    $ttl = Duration::fromHours(1);

    $result = $this->cache->set(CacheItem::forSet($key, $value, $ttl));

    expect($result)->toBeTrue();

    $cacheResult = $this->cache->get($key);

    expect($cacheResult->isHit)->toBeTrue()
        ->and($cacheResult->value)->toBe($value);
});

test('multiple keys can be stored independently', function () {
    $key1 = CacheKey::fromString('key1');
    $key2 = CacheKey::fromString('key2');
    $key3 = CacheKey::fromString('key3');

    $this->cache->set(CacheItem::forSet($key1, 'value1'));
    $this->cache->set(CacheItem::forSet($key2, 'value2'));
    $this->cache->set(CacheItem::forSet($key3, 'value3'));

    expect($this->cache->get($key1)->value)->toBe('value1')
        ->and($this->cache->get($key2)->value)->toBe('value2')
        ->and($this->cache->get($key3)->value)->toBe('value3');
});

test('overwriting existing key updates value', function () {
    $key = CacheKey::fromString('test-key');

    $this->cache->set(CacheItem::forSet($key, 'original-value'));
    expect($this->cache->get($key)->value)->toBe('original-value');

    $this->cache->set(CacheItem::forSet($key, 'updated-value'));
    expect($this->cache->get($key)->value)->toBe('updated-value');
});
