<?php

/**
 * JCH Optimize - Performs several front-end optimizations for fast downloads.
 *
 * @author    Samuel Marshall <samuel@jch-optimize.net>
 * @copyright Copyright (c) 2022 Samuel Marshall / JCH Optimize
 * @license   GNU/GPLv3, or later. See LICENSE file
 *
 *  If LICENSE file missing, see <http://www.gnu.org/licenses/>.
 */

namespace JchOptimize\Core\Model;

use _JchOptimizeVendor\Laminas\Cache\Exception\ExceptionInterface;
use _JchOptimizeVendor\Laminas\Cache\Storage\Adapter\Filesystem;
use _JchOptimizeVendor\Laminas\Cache\Storage\Adapter\FilesystemOptions;
use _JchOptimizeVendor\Laminas\Cache\Storage\ClearByNamespaceInterface;
use _JchOptimizeVendor\Laminas\Cache\Storage\FlushableInterface;
use _JchOptimizeVendor\Laminas\Cache\Storage\IterableInterface;
use JchOptimize\Core\Helper;
use JchOptimize\Platform\Cache;
use JchOptimize\Platform\Paths;
use Joomla\Filesystem\Folder;

\defined('_JCH_EXEC') or exit('Restricted access');
trait CacheModelTrait
{
    protected int $size = 0;
    protected int $numFiles = 0;

    public function getCacheSize(): array
    {
        if ($this->cache instanceof IterableInterface) {
            $this->getIterableCacheSize($this->cache);
        }
        if ($this->pageCacheStorage instanceof IterableInterface) {
            $this->getIterableCacheSize($this->pageCacheStorage);
        }
        // Iterate through the static files
        if (\file_exists(Paths::cachePath(\false))) {
            $directory = new \RecursiveDirectoryIterator(Paths::cachePath(\false), \FilesystemIterator::SKIP_DOTS);
            $iterator = new \RecursiveIteratorIterator($directory);
            $i = 0;
            foreach ($iterator as $file) {
                if (\in_array($file->getFilename(), ['index.html', '.htaccess'])) {
                    ++$i;

                    continue;
                }
                $this->size += $file->getSize();
            }
            $this->numFiles += \iterator_count($iterator) - $i;
        }
        $decimals = 2;
        $sz = 'BKMGTP';
        $factor = (int) \floor((\strlen((string) $this->size) - 1) / 3);
        $size = \sprintf("%.{$decimals}f", $this->size / \pow(1024, $factor)).\str_split($sz)[$factor];
        $numFiles = \number_format($this->numFiles);

        return [$size, $numFiles];
    }

    /**
     * Cleans cache from the server.
     */
    public function cleanCache(): bool
    {
        $success = 1;
        // First try to delete the Http request cache
        // Delete any static combined files
        $staticCachePath = Paths::cachePath(\false);

        try {
            if (\file_exists($staticCachePath)) {
                Folder::delete($staticCachePath);
            }
        } catch (\Exception $e) {
            try {
                // Didn't work, Joomla can't handle paths containing backslash, let's try another way
                Helper::deleteFolder($staticCachePath);
            } catch (\Exception $e) {
            }
        }
        $success &= (int) (!\file_exists($staticCachePath));

        try {
            // Clean all cache generated by Storage
            if ($this->cache instanceof ClearByNamespaceInterface) {
                $success &= (int) $this->cache->clearByNamespace(Cache::getCacheNamespace());
            } elseif ($this->cache instanceof FlushableInterface) {
                $success &= (int) $this->cache->flush();
            }
            // And page cache
            if ($this->pageCacheStorage instanceof ClearByNamespaceInterface) {
                $success &= (int) $this->pageCacheStorage->clearByNamespace(Cache::getCacheNamespace(\true));
            } elseif ($this->cache instanceof FlushableInterface) {
                $success &= (int) $this->pageCache->deleteAllItems();
            }
        } catch (\Exception $e) {
            $success = \false;
        }
        // If all goes well, also delete tags
        if ($success) {
            if ($this->taggableCache instanceof ClearByNamespaceInterface) {
                $this->taggableCache->clearByNamespace('jchoptimizetags');
            } elseif ($this->taggableCache instanceof FlushableInterface) {
                $this->taggableCache->flush();
            }
        }
        // Clean third party cache
        Cache::cleanThirdPartyPageCache();

        return (bool) $success;
    }

    private function getIterableCacheSize($cache): void
    {
        try {
            $iterator = $cache->getIterator();
            $this->numFiles += \iterator_count($iterator);
            foreach ($iterator as $item) {
                // Let's skip the 'test' cache set on instantiation in container
                if ($item == \md5('__ITEM__')) {
                    --$this->numFiles;

                    continue;
                }
                $metaData = $cache->getMetadata($item);
                if (!\is_array($metaData)) {
                    continue;
                }
                if (isset($metaData['size'])) {
                    $this->size += $metaData['size'];
                } elseif ($cache instanceof Filesystem) {
                    /** @var FilesystemOptions $cacheOptions */
                    $cacheOptions = $cache->getOptions();
                    $suffix = $cacheOptions->getSuffix();
                    if (isset($metaData['filespec']) && \file_exists($metaData['filespec'].'.'.$suffix)) {
                        $this->size += \filesize($metaData['filespec'].'.'.$suffix);
                    }
                }
            }
        } catch (ExceptionInterface|\Exception $e) {
        }
    }
}
