<?php

namespace Velis\Test\Cache;

use Phalcon\Cache\Adapter\AdapterInterface;
use Phalcon\Proxy\Psr16\Cache;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Velis\App;
use Velis\App\PhalconVersion;
use Velis\Cache\RedisCache;

/**
 * @author Jan Małysiak <jan.malysiak@velis.pl>
 */
class RedisCacheTest extends TestCase
{
    /**
     * @var RedisCache
     */
    private $cache;

    /**
     * @var MockObject
     */
    private $implementationMock;

    /**
     * {@inheritDoc}
     */
    protected function setUp(): void
    {
        $adapterMock = $this->createMock(AdapterInterface::class);
        $adapterMock->method('getKeys')
            ->willReturn(['123', '456']);

        if (App::getPhalconMajorVersion() >= PhalconVersion::PHALCON5) {
            $cacheClass = Cache::class;
        } else {
            $cacheClass = \Phalcon\Cache::class;
        }

        $this->implementationMock = $this->createMock($cacheClass);
        $this->implementationMock->method('getAdapter')
            ->willReturn($adapterMock);

        $this->cache = new RedisCache($this->implementationMock, true);
    }

    public function testGetWhenEntryDoesNotExist()
    {
        $this->implementationMock->expects($this->once())
            ->method('has')
            ->willReturn(false);

        $this->implementationMock->expects($this->never())
            ->method('get');

        $result = $this->cache->get('key');

        $this->assertNull($result);
    }

    public function testGetWhenEntryExists()
    {
        $key = 'key';
        $expectedValue = 'value';

        $this->implementationMock->expects($this->once())
            ->method('has')
            ->willReturn(true);

        $this->implementationMock->expects($this->once())
            ->method('get')
            ->willReturn($expectedValue);

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

        $this->assertEquals($expectedValue, $actualValue);

        // next calls should read from buffer instead of implementation
        $actualValue2 = $this->cache->get($key);

        $this->assertEquals($expectedValue, $actualValue2);
    }

    public function testSet()
    {
        $expectedValue = 'value';

        $this->implementationMock->expects($this->once())
            ->method('set');

        $this->cache->set('key', $expectedValue);

        // check if `get` method reads from buffer
        $this->implementationMock->expects($this->never())
            ->method('has');

        $this->implementationMock->expects($this->never())
            ->method('get');

        $actualValue = $this->cache->get('key');

        $this->assertEquals($expectedValue, $actualValue);
    }

    public function testUnsetBuffer()
    {
        $key = 'key';
        $expectedValue = 'value';

        $this->implementationMock->expects($this->exactly(2))
            ->method('has')
            ->willReturn(true);

        $this->implementationMock->expects($this->exactly(2))
            ->method('get')
            ->willReturn($expectedValue);

        $this->cache->get($key);
        $this->cache->unsetBuffer();
        $actualValue = $this->cache->get($key);

        $this->assertEquals($expectedValue, $actualValue);
    }

    public function testGetKeys()
    {
        $keys = $this->cache->getKeys();

        self::assertIsArray($keys);
        self::assertNotEmpty($keys);

        $key = reset($keys);
        self::assertIsString($key);
    }
}
