<?php

namespace Velis\Cache;

use Exception;
use InvalidArgumentException;
use Psr\SimpleCache\CacheInterface as PsrCacheInterface;
use Throwable;
use Velis\Arrays;
use Velis\Filesystem\Filesystem;

/**
 * File based cache
 *
 * @author Jan Małysiak <jan.malysiak@velis.pl>
 */
class FileCache extends AbstractCache
{
    private string $directory;

    /**
     * @param PsrCacheInterface $implementation
     * @param bool $debug
     * @param string $directory
     */
    public function __construct(PsrCacheInterface $implementation, $debug = false, string $directory = 'cache')
    {
        $this->directory = $directory;
        parent::__construct($implementation, $debug);
    }

    /**
     * {@inheritDoc}
     */
    public function get($key, $default = null)
    {
        $this->initializeProfiler($key, __FUNCTION__);

        $result = null;

        if ($this->implementation->has($key)) {
            try {
                $result = $this->implementation->get($key, $default);
                $result = $this->filterEmptyArray($result);
            } catch (Throwable $e) {
                $result = null;
            }
        }

        $this->concludeProfiler($key);

        return $result;
    }

    /**
     * {@inheritDoc}
     */
    public function set($key, $value, $ttl = null)
    {
        $this->initializeProfiler($key, __FUNCTION__);
        $dataToSet = $value;

        if (is_array($dataToSet) && empty($dataToSet)) {
            $dataToSet = Arrays::NULL_ARRAY;
        }

        try {
            $result = $this->implementation->set($key, $dataToSet, $ttl);
        } catch (Exception $e) {
            $this->implementation->delete($key);
            $result = $this->implementation->set($key, $dataToSet, $ttl);
        }

        $this->concludeProfiler($key);

        return $result;
    }

    /**
     * {@inheritDoc}
     */
    public function clear()
    {
        try {
            $filesystem = new Filesystem(DATA_PATH . $this->directory);
            $cacheKeys = $filesystem->listKeys();

            foreach ($cacheKeys as $key) {
                if ('.gitignore' == $key) {
                    continue;
                }

                $filesystem->delete($key);
            }
        } catch (Exception $e) {
            return false;
        }

        return true;
    }

    /**
     * {@inheritDoc}
     */
    public function getKeys(string $prefix = ''): array
    {
        try {
            $filesystem = new Filesystem(DATA_PATH . $this->directory);
            $keys = [];

            foreach ($filesystem->listKeys() as $key) {
                if (preg_match('/' . $prefix . '.*/i', $key, $matches)) {
                    $keys[] = basename($matches[0]);
                }
            }

            return $keys;
        } catch (Exception $e) {
            return [];
        }
    }

    /**
     * {@inheritDoc}
     * @throws InvalidArgumentException
     */
    public function offsetSet($offset, $value): void
    {
        $this->implementation->set($offset, $value);
    }
}
