<?php

namespace App\Tests\Service;

use App\Enum\CacheKey;
use App\Service\CacheService;
use PHPUnit\Framework\TestCase;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;

class CacheServiceTest extends TestCase
{
    private CacheItemPoolInterface $pool;
    private CacheService $service;

    protected function setUp(): void
    {
        $this->pool = $this->createMock(CacheItemPoolInterface::class);
        $this->service = new CacheService($this->pool);
    }

    private function mockCacheItem(bool $isHit, mixed $value = null): CacheItemInterface
    {
        $item = $this->createMock(CacheItemInterface::class);
        $item->method('isHit')->willReturn($isHit);
        $item->method('get')->willReturn($value);
        $item->method('set')->willReturnSelf();
        $item->method('expiresAfter')->willReturnSelf();

        return $item;
    }

    public function testGetReturnsNullOnCacheMiss(): void
    {
        $this->pool->method('getItem')->willReturn($this->mockCacheItem(false));

        self::assertNull($this->service->get(CacheKey::HOME_PAGE));
    }

    public function testGetReturnsValueOnCacheHit(): void
    {
        $this->pool->method('getItem')->willReturn($this->mockCacheItem(true, 'cached-data'));

        self::assertSame('cached-data', $this->service->get(CacheKey::HOME_PAGE));
    }

    public function testSetStoresValueWithTtl(): void
    {
        $item = $this->mockCacheItem(false);
        $this->pool->method('getItem')->willReturn($item);
        $this->pool->expects(self::once())->method('save')->with($item)->willReturn(true);

        $this->service->set(CacheKey::HOME_PAGE, 'value');
    }

    public function testExistsDelegatesToHasItem(): void
    {
        $this->pool->expects(self::once())->method('hasItem')->willReturn(true);

        self::assertTrue($this->service->exists(CacheKey::HOME_PAGE));
    }

    public function testDeleteDelegatesToDeleteItem(): void
    {
        $this->pool->expects(self::once())->method('deleteItem')->willReturn(true);

        $this->service->delete(CacheKey::HOME_PAGE);
    }

    public function testRememberReturnsCachedValueOnHit(): void
    {
        $this->pool->method('getItem')->willReturn($this->mockCacheItem(true, 'cached'));

        $result = $this->service->remember(CacheKey::HOME_PAGE, fn () => 'fresh');

        self::assertSame('cached', $result);
    }

    public function testRememberCallsCallbackOnMiss(): void
    {
        $item = $this->mockCacheItem(false);
        $this->pool->method('getItem')->willReturn($item);
        $this->pool->expects(self::once())->method('save')->with($item)->willReturn(true);

        $result = $this->service->remember(CacheKey::HOME_PAGE, fn () => 'fresh');

        self::assertSame('fresh', $result);
    }
}
