<?php

declare(strict_types=1);

namespace App\Framework\DI\Dependency;

use App\Framework\Core\ValueObjects\ClassName;
use App\Framework\DI\Container;

/**
 * Type-safe collection of parameter cache information
 */
final readonly class ParameterCacheCollection implements \IteratorAggregate, \Countable
{
    /**
     * @param array<ParameterCacheInfo> $parameters
     */
    private function __construct(
        private array $parameters
    ) {
    }

    /**
     * Create collection from ParameterCacheInfo objects
     * @param ParameterCacheInfo ...$parameters
     */
    public static function from(ParameterCacheInfo ...$parameters): self
    {
        return new self($parameters);
    }

    /**
     * Create collection from legacy cache array format
     * @param array<array<string, mixed>> $cacheArray
     */
    public static function fromArray(array $cacheArray): self
    {
        $parameters = array_map(
            fn (array $info) => ParameterCacheInfo::fromArray($info),
            $cacheArray
        );

        return new self($parameters);
    }

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

    /**
     * Get iterator for foreach loops
     * @return \ArrayIterator<int, ParameterCacheInfo>
     */
    public function getIterator(): \ArrayIterator
    {
        return new \ArrayIterator($this->parameters);
    }

    /**
     * Count parameters
     */
    public function count(): int
    {
        return count($this->parameters);
    }

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

    /**
     * Get parameter at specific position
     */
    public function get(int $position): ?ParameterCacheInfo
    {
        return $this->parameters[$position] ?? null;
    }

    /**
     * Get all parameters as array
     * @return array<ParameterCacheInfo>
     */
    public function toArray(): array
    {
        return $this->parameters;
    }

    /**
     * Convert to legacy array format (for backward compatibility)
     * @return array<array<string, mixed>>
     */
    public function toLegacyArray(): array
    {
        return array_map(
            fn (ParameterCacheInfo $param) => $param->toArray(),
            $this->parameters
        );
    }

    /**
     * Get all dependency types used
     * @return array<DependencyType>
     */
    public function getDependencyTypes(): array
    {
        $types = [];
        foreach ($this->parameters as $param) {
            if (! in_array($param->type, $types, true)) {
                $types[] = $param->type;
            }
        }

        return $types;
    }

    /**
     * Check if collection uses specific dependency type
     */
    public function usesDependencyType(DependencyType $type): bool
    {
        foreach ($this->parameters as $param) {
            if ($param->type === $type) {
                return true;
            }
        }

        return false;
    }

    /**
     * Get all class dependencies
     * @return array<ClassName>
     */
    public function getClassDependencies(): array
    {
        $dependencies = [];
        foreach ($this->parameters as $param) {
            if ($param->hasClassDependency()) {
                $dependencies[] = $param->className;
            }
        }

        return $dependencies;
    }

    /**
     * Check if any parameter depends on specific class
     */
    public function dependsOn(ClassName $className): bool
    {
        foreach ($this->parameters as $param) {
            if ($param->dependsOn($className)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Filter parameters by dependency type
     */
    public function filterByType(DependencyType $type): self
    {
        $filtered = array_filter(
            $this->parameters,
            fn (ParameterCacheInfo $param) => $param->type === $type
        );

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

    /**
     * Get parameters that have class dependencies
     */
    public function getClassDependencyParameters(): self
    {
        $filtered = array_filter(
            $this->parameters,
            fn (ParameterCacheInfo $param) => $param->hasClassDependency()
        );

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

    /**
     * Resolve all parameter values using container
     * @return array<mixed>
     */
    public function resolveValues(Container $container): array
    {
        return array_map(
            fn (ParameterCacheInfo $param) => $param->resolveValue($container),
            $this->parameters
        );
    }
}
