<?php

declare(strict_types=1);

namespace PpPhp\Core\Analytics;

/**
 * A tiny file-backed cache for the owner Visitor console's aggregate bundle.
 *
 * The overview fires ~19 full-table GROUP-BY aggregates on every load (and the
 * live-poll re-runs a couple). None of those numbers need to be to-the-second
 * for an owner-only dashboard, so caching the whole bundle for a short TTL turns
 * a page refresh from ~19 scans into a single file read. Best-effort: any I/O
 * failure just falls back to computing live.
 */
final class AnalyticsCache
{
    private static function dir(): string
    {
        return (defined('BASE_PATH') ? constant('BASE_PATH') : sys_get_temp_dir()) . '/storage/cache';
    }

    private static function path(string $key): string
    {
        return self::dir() . '/an-' . preg_replace('/[^a-z0-9_-]/i', '', $key) . '.cache';
    }

    /**
     * Return the cached value for $key if fresh (< $ttl seconds old), else run
     * $producer, store its result, and return it.
     *
     * @param callable():array $producer
     * @return array<string,mixed>
     */
    public static function remember(string $key, int $ttl, callable $producer): array
    {
        $file = self::path($key);
        if (is_file($file) && (time() - (int) @filemtime($file)) < max(1, $ttl)) {
            $raw = @file_get_contents($file);
            if (is_string($raw) && $raw !== '') {
                $data = @unserialize($raw, ['allowed_classes' => false]);
                if (is_array($data)) return $data;
            }
        }
        $data = $producer();
        try {
            $dir = self::dir();
            if (!is_dir($dir)) @mkdir($dir, 0775, true);
            @file_put_contents($file, serialize($data), LOCK_EX);
        } catch (\Throwable $e) { /* cache is best-effort */ }
        return $data;
    }

    /** Drop every analytics cache file — call after any mutation so the console
     *  reflects the change on the very next load instead of after the TTL. */
    public static function flush(): void
    {
        foreach ((array) @glob(self::dir() . '/an-*.cache') as $f) { @unlink((string) $f); }
    }
}
