<?php

declare(strict_types=1);

namespace App\Framework\LiveComponents\Caching;

/**
 * Cache Metrics Value Object
 *
 * Tracks cache performance metrics for LiveComponents.
 *
 * Metrics:
 * - Hit rate: Percentage of cache hits
 * - Age: Time since content was cached
 * - Stale status: Whether content is stale but valid (SWR)
 * - Refresh status: Whether background refresh is in progress
 *
 * Framework Pattern: Immutable readonly Value Object
 */
final readonly class CacheMetrics
{
    /**
     * @param bool $hit - Cache hit (true) or miss (false)
     * @param int $ageSeconds - Age of cached content in seconds
     * @param bool $isStale - Content is stale but served via SWR
     * @param bool $isRefreshing - Background refresh in progress
     * @param int|null $cachedAt - Unix timestamp when cached
     * @param int|null $expiresAt - Unix timestamp when expires
     * @param string|null $cacheKey - Cache key used
     */
    public function __construct(
        public bool $hit,
        public int $ageSeconds = 0,
        public bool $isStale = false,
        public bool $isRefreshing = false,
        public ?int $cachedAt = null,
        public ?int $expiresAt = null,
        public ?string $cacheKey = null
    ) {
    }

    /**
     * Create metrics for cache miss
     */
    public static function miss(?string $cacheKey = null): self
    {
        return new self(
            hit: false,
            cacheKey: $cacheKey
        );
    }

    /**
     * Create metrics for cache hit
     */
    public static function hit(
        int $cachedAt,
        int $expiresAt,
        string $cacheKey
    ): self {
        $now = time();
        $age = $now - $cachedAt;

        return new self(
            hit: true,
            ageSeconds: $age,
            isStale: false,
            cachedAt: $cachedAt,
            expiresAt: $expiresAt,
            cacheKey: $cacheKey
        );
    }

    /**
     * Create metrics for stale cache hit (SWR)
     */
    public static function staleHit(
        int $cachedAt,
        int $expiresAt,
        string $cacheKey,
        bool $isRefreshing = false
    ): self {
        $now = time();
        $age = $now - $cachedAt;

        return new self(
            hit: true,
            ageSeconds: $age,
            isStale: true,
            isRefreshing: $isRefreshing,
            cachedAt: $cachedAt,
            expiresAt: $expiresAt,
            cacheKey: $cacheKey
        );
    }

    /**
     * Check if content is expired
     */
    public function isExpired(): bool
    {
        if ($this->expiresAt === null) {
            return false;
        }

        return time() > $this->expiresAt;
    }

    /**
     * Get remaining TTL in seconds
     */
    public function getRemainingTTL(): int
    {
        if ($this->expiresAt === null) {
            return 0;
        }

        $remaining = $this->expiresAt - time();

        return max(0, $remaining);
    }

    /**
     * Get freshness percentage (0-100)
     *
     * 100 = just cached, 0 = expired
     */
    public function getFreshnessPercent(): int
    {
        if (! $this->hit || $this->cachedAt === null || $this->expiresAt === null) {
            return 0;
        }

        $totalTTL = $this->expiresAt - $this->cachedAt;
        if ($totalTTL <= 0) {
            return 0;
        }

        $remaining = $this->getRemainingTTL();
        $percent = (int) (($remaining / $totalTTL) * 100);

        return max(0, min(100, $percent));
    }

    /**
     * Convert to array for logging/debugging
     */
    public function toArray(): array
    {
        return [
            'hit' => $this->hit,
            'age_seconds' => $this->ageSeconds,
            'is_stale' => $this->isStale,
            'is_refreshing' => $this->isRefreshing,
            'cached_at' => $this->cachedAt ? date('Y-m-d H:i:s', $this->cachedAt) : null,
            'expires_at' => $this->expiresAt ? date('Y-m-d H:i:s', $this->expiresAt) : null,
            'remaining_ttl' => $this->getRemainingTTL(),
            'freshness_percent' => $this->getFreshnessPercent(),
            'cache_key' => $this->cacheKey,
        ];
    }

    /**
     * Get human-readable status
     */
    public function getStatusText(): string
    {
        if (! $this->hit) {
            return 'MISS';
        }

        if ($this->isStale && $this->isRefreshing) {
            return 'STALE (Refreshing)';
        }

        if ($this->isStale) {
            return 'STALE';
        }

        return 'HIT';
    }
}
