<?php

declare(strict_types=1);

namespace App\Framework\Cache;

use Countable;
use IteratorAggregate;
use Traversable;

/**
 * Collection of cache keys for batch operations
 */
final readonly class CacheKeyCollection implements Countable, IteratorAggregate
{
    /**
     * @param array<CacheKey> $keys
     */
    private function __construct(
        private array $keys
    ) {
    }

    /**
     * Create from CacheKeys using spread operator
     */
    public static function fromKeys(CacheKey ...$keys): self
    {
        return new self($keys);
    }

    /**
     * Create empty collection
     */
    public static function empty(): self
    {
        return new self([]);
    }

    /**
     * Get all keys as array
     * @return array<CacheKey>
     */
    public function getKeys(): array
    {
        return $this->keys;
    }

    /**
     * Get first key or null if empty
     */
    public function first(): ?CacheKey
    {
        return $this->keys[0] ?? null;
    }

    /**
     * Get last key or null if empty
     */
    public function last(): ?CacheKey
    {
        return end($this->keys) ?: null;
    }

    /**
     * Check if collection contains specific key
     */
    public function contains(CacheKey $key): bool
    {
        foreach ($this->keys as $existingKey) {
            if ($existingKey->equals($key)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Filter keys by predicate
     */
    public function filter(callable $predicate): self
    {
        $filtered = array_filter($this->keys, $predicate);

        return new self(array_values($filtered));
    }

    /**
     * Map over keys
     */
    public function map(callable $mapper): self
    {
        $mapped = array_map($mapper, $this->keys);

        return new self($mapped);
    }

    /**
     * Add another key collection
     */
    public function merge(self $other): self
    {
        return new self(array_merge($this->keys, $other->keys));
    }

    /**
     * Add a single key
     */
    public function add(CacheKey $key): self
    {
        return new self([...$this->keys, $key]);
    }

    /**
     * Remove duplicates based on string representation
     */
    public function unique(): self
    {
        $unique = [];
        $seen = [];

        foreach ($this->keys as $key) {
            $keyString = $key->toString();
            if (! isset($seen[$keyString])) {
                $unique[] = $key;
                $seen[$keyString] = true;
            }
        }

        return new self($unique);
    }

    /**
     * Check if collection is empty
     */
    public function isEmpty(): bool
    {
        return empty($this->keys);
    }

    /**
     * Get keys as string array
     * @return array<string>
     */
    public function toStringArray(): array
    {
        return array_map(fn (CacheKey $key) => $key->toString(), $this->keys);
    }

    /**
     * Countable implementation
     */
    public function count(): int
    {
        return count($this->keys);
    }

    /**
     * IteratorAggregate implementation
     * @return Traversable<int, CacheKey>
     */
    public function getIterator(): Traversable
    {
        foreach ($this->keys as $key) {
            yield $key;
        }
    }
}
