<?php

declare(strict_types=1);

namespace App\Framework\Database\Cache;

/**
 * Cache key generator for entities
 */
final readonly class EntityCacheKey
{
    public function __construct(
        public string $entityClass,
        public mixed $entityId,
        public ?string $region = null
    ) {
    }

    /**
     * Create cache key for single entity
     */
    public static function forEntity(string $entityClass, mixed $entityId, ?string $region = null): self
    {
        return new self($entityClass, $entityId, $region);
    }

    /**
     * Create cache key for entity collection
     */
    public static function forCollection(string $entityClass, array $criteria = [], ?array $orderBy = null, ?int $limit = null, ?int $offset = null): string
    {
        $keyParts = [
            'collection',
            self::getShortClassName($entityClass),
            md5(serialize($criteria)),
        ];

        if ($orderBy !== null) {
            $keyParts[] = md5(serialize($orderBy));
        }

        if ($limit !== null) {
            $keyParts[] = "limit:{$limit}";
        }

        if ($offset !== null) {
            $keyParts[] = "offset:{$offset}";
        }

        return implode(':', $keyParts);
    }

    /**
     * Create cache key for query result
     */
    public static function forQuery(string $sql, array $parameters = []): string
    {
        $paramHash = empty($parameters) ? 'no-params' : md5(serialize($parameters));
        $sqlHash = md5($sql);

        return "query:{$sqlHash}:{$paramHash}";
    }

    /**
     * Create cache key for relation
     */
    public static function forRelation(string $ownerClass, mixed $ownerId, string $relationName): string
    {
        $ownerClassName = self::getShortClassName($ownerClass);

        return "relation:{$ownerClassName}:{$ownerId}:{$relationName}";
    }

    /**
     * Generate the actual cache key string
     */
    public function toString(): string
    {
        $className = self::getShortClassName($this->entityClass);
        $keyParts = ['entity', $className, (string) $this->entityId];

        if ($this->region !== null) {
            array_unshift($keyParts, $this->region);
        }

        return implode(':', $keyParts);
    }

    /**
     * Get versioned cache key (includes entity version for optimistic locking)
     */
    public function toVersionedString(int $version): string
    {
        return $this->toString() . ':v' . $version;
    }

    /**
     * Get invalidation pattern for entity class
     */
    public static function getEntityClassPattern(string $entityClass, ?string $region = null): string
    {
        $className = self::getShortClassName($entityClass);
        $pattern = "entity:{$className}:*";

        if ($region !== null) {
            $pattern = "{$region}:{$pattern}";
        }

        return $pattern;
    }

    /**
     * Get invalidation pattern for relations
     */
    public static function getRelationPattern(string $ownerClass, mixed $ownerId): string
    {
        $ownerClassName = self::getShortClassName($ownerClass);

        return "relation:{$ownerClassName}:{$ownerId}:*";
    }

    private static function getShortClassName(string $entityClass): string
    {
        $parts = explode('\\', $entityClass);

        return end($parts);
    }

    public function __toString(): string
    {
        return $this->toString();
    }
}
