<?php

namespace App\Services;

/**
 * File-based full-page cache.
 *
 * Stores rendered HTML in storage/cache/pages/ keyed by request URI.
 * Skips caching for POST requests, admin paths, and logged-in users.
 */
class PageCache
{
    private string $cacheDir;
    private int $ttl;

    public function __construct(string $basePath, int $ttl = 3600)
    {
        $this->cacheDir = $basePath . '/storage/cache/pages';
        $this->ttl      = $ttl;

        if (!is_dir($this->cacheDir)) {
            @mkdir($this->cacheDir, 0755, true);
        }
    }

    /**
     * Get cached HTML for the current request, or null if not cached / expired.
     */
    public function get(string $uri): ?string
    {
        $file = $this->cacheFile($uri);
        if (!is_file($file)) {
            return null;
        }

        // Check TTL
        if ((time() - filemtime($file)) > $this->ttl) {
            @unlink($file);
            return null;
        }

        $content = file_get_contents($file);
        return $content !== false ? $content : null;
    }

    /**
     * Store rendered HTML in cache.
     */
    public function set(string $uri, string $html): void
    {
        $file = $this->cacheFile($uri);
        $dir  = dirname($file);

        if (!is_dir($dir)) {
            @mkdir($dir, 0755, true);
        }

        // Atomic write via temp file
        $tmp = $file . '.tmp.' . getmypid();
        if (file_put_contents($tmp, $html, LOCK_EX) !== false) {
            rename($tmp, $file);
        }
    }

    /**
     * Check if the current request should be cached.
     */
    public static function shouldCache(): bool
    {
        // Skip POST/PUT/DELETE
        $method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
        if ($method !== 'GET' && $method !== 'HEAD') {
            return false;
        }

        $uri = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?? '/';

        // Skip admin, installer, API paths
        if (str_starts_with($uri, '/admin') || str_starts_with($uri, '/installer')) {
            return false;
        }

        // Skip logged-in users (session check)
        if (session_status() === PHP_SESSION_ACTIVE && !empty($_SESSION['user_id'])) {
            return false;
        }

        // Skip if query string contains preview/debug params
        if (isset($_GET['preview']) || isset($_GET['nocache'])) {
            return false;
        }

        return true;
    }

    /**
     * Clear the entire page cache.
     */
    public function clear(): int
    {
        return $this->clearDir($this->cacheDir);
    }

    /**
     * Clear cache for a specific URI.
     */
    public function clearUri(string $uri): void
    {
        $file = $this->cacheFile($uri);
        if (is_file($file)) {
            @unlink($file);
        }
    }

    /**
     * Get cache directory path.
     */
    public function getCacheDir(): string
    {
        return $this->cacheDir;
    }

    /**
     * Get number of cached pages.
     */
    public function count(): int
    {
        if (!is_dir($this->cacheDir)) {
            return 0;
        }
        $count = 0;
        $iter  = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($this->cacheDir, \FilesystemIterator::SKIP_DOTS)
        );
        foreach ($iter as $file) {
            if ($file->isFile() && $file->getExtension() === 'html') {
                $count++;
            }
        }
        return $count;
    }

    // ─────────────────────────────────────────────────────────────────────────

    private function cacheFile(string $uri): string
    {
        // Normalize URI
        $uri = '/' . ltrim($uri, '/');
        $uri = strtok($uri, '?'); // strip query string

        // Create a safe file path from the URI
        if ($uri === '/') {
            return $this->cacheDir . '/_home.html';
        }

        $safe = trim($uri, '/');
        $safe = preg_replace('/[^a-zA-Z0-9\/_-]/', '_', $safe);

        return $this->cacheDir . '/' . $safe . '.html';
    }

    private function clearDir(string $dir): int
    {
        if (!is_dir($dir)) {
            return 0;
        }

        $count = 0;
        $iter  = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS),
            \RecursiveIteratorIterator::CHILD_FIRST
        );

        foreach ($iter as $item) {
            if ($item->isFile()) {
                @unlink($item->getPathname());
                $count++;
            } elseif ($item->isDir()) {
                @rmdir($item->getPathname());
            }
        }

        return $count;
    }
}
