<?php

declare(strict_types=1);

namespace App\Framework\Cache;

use InvalidArgumentException;

/**
 * Cache tag identifier for grouping and batch operations
 * Allows invalidating multiple cache items by tag
 */
final readonly class CacheTag implements CacheIdentifier
{
    private const int MAX_TAG_LENGTH = 100;
    private const string TAG_PREFIX = 'tag:';

    private function __construct(
        private string $tag
    ) {
        $this->validate($tag);
    }

    /**
     * Create cache tag from string
     */
    public static function fromString(string $tag): self
    {
        return new self($tag);
    }

    /**
     * Create cache tag for specific domain/type
     */
    public static function forType(string $type): self
    {
        return new self($type);
    }

    /**
     * Create cache tag for user-related items
     */
    public static function forUser(string|int $userId): self
    {
        return new self("user:{$userId}");
    }

    /**
     * Create cache tag for entity type
     */
    public static function forEntity(string $entityType): self
    {
        return new self("entity:{$entityType}");
    }

    /**
     * Get string representation of the tag
     */
    public function toString(): string
    {
        return $this->tag;
    }

    /**
     * Get the type of cache identifier
     */
    public function getType(): CacheIdentifierType
    {
        return CacheIdentifierType::TAG;
    }

    /**
     * Check if this tag equals another identifier
     */
    public function equals(CacheIdentifier $other): bool
    {
        return $other instanceof self && $this->tag === $other->tag;
    }

    /**
     * Check if this tag matches a cache key
     * Tags match keys that contain the tag in their metadata
     */
    public function matchesKey(CacheKey $key): bool
    {
        // This would need to be implemented by checking key metadata/tags
        // For now, simple string containment check
        return str_contains($key->toString(), $this->tag);
    }

    /**
     * Get a normalized string for internal cache operations
     */
    public function getNormalizedString(): string
    {
        return self::TAG_PREFIX . $this->tag;
    }

    /**
     * Create a cache key that includes this tag
     */
    public function createKeyWithTag(string $baseKey): CacheKey
    {
        return CacheKey::fromString($baseKey . ':' . $this->tag);
    }

    /**
     * Check if tag matches a pattern
     */
    public function matchesPattern(string $pattern): bool
    {
        return fnmatch($pattern, $this->tag);
    }

    /**
     * Validate the tag
     */
    private function validate(string $tag): void
    {
        if (empty($tag)) {
            throw new InvalidArgumentException('Cache tag cannot be empty');
        }

        if (strlen($tag) > self::MAX_TAG_LENGTH) {
            throw new InvalidArgumentException(sprintf(
                'Cache tag length exceeds maximum of %d characters (got %d)',
                self::MAX_TAG_LENGTH,
                strlen($tag)
            ));
        }

        // Check for invalid characters (allow colons for namespacing)
        if (preg_match('/[\s\n\r\t\0\x0B*?]/', $tag)) {
            throw new InvalidArgumentException('Cache tag contains invalid characters');
        }
    }
}
