<?php

declare(strict_types=1);

namespace App\Framework\Reflection;

use App\Framework\Core\ValueObjects\ClassName;
use App\Framework\Reflection\Cache\ClassCache;
use App\Framework\Reflection\Collections\AttributeCollection;
use App\Framework\Reflection\Collections\MethodCollection;
use App\Framework\Reflection\Collections\PropertyCollection;
use App\Framework\Reflection\Support\AttributeHandler;
use ReflectionClass;

final readonly class WrappedReflectionClass
{
    private AttributeHandler $attributeHandler;

    public function __construct(
        private ClassName $className,
        private ClassCache $cache
    ) {
        $this->attributeHandler = new AttributeHandler();
    }

    public function getProperties(): PropertyCollection
    {
        return $this->cache->getProperties($this->className);
    }

    public function getName(): string
    {
        return $this->className->getFullyQualified();
    }

    public function getShortName(): string
    {
        return $this->cache->getShortName($this->className);
    }

    public function isInstantiable(): bool
    {
        return $this->cache->isInstantiable($this->className);
    }

    public function implementsInterface(string $interface): bool
    {
        return $this->cache->implementsInterface($this->className, $interface);
    }

    public function getAttributes(?string $attributeClass = null): AttributeCollection
    {
        return $this->cache->getAttributes($this->className, $attributeClass);
    }

    // Erweiterte Convenience-Methoden
    public function getAttributeInstances(?string $attributeClass = null): array
    {
        $attributes = $this->cache->getAttributes($this->className, $attributeClass);

        return $this->attributeHandler->getInstances($attributes);
    }

    public function hasAttribute(string $attributeClass): bool
    {
        return $this->cache->hasAttribute($this->className, $attributeClass);
    }

    public function getFirstAttribute(string $attributeClass): ?object
    {
        $attributes = $this->cache->getAttributes($this->className, $attributeClass);

        return $this->attributeHandler->getFirst($attributes);
    }

    public function getMethods(?int $filter = null): MethodCollection
    {
        return $this->cache->getMethods($this->className, $filter);
    }

    public function getMethodsWithAttribute(string $attributeClass): MethodCollection
    {
        return $this->cache->getMethods($this->className)->getWithAttribute($attributeClass);
    }

    public function getMethod(string $name): WrappedReflectionMethod
    {
        return $this->cache->getMethod($this->className, $name);
    }

    /**
     * Create a new instance with arguments - delegates to native ReflectionClass
     *
     * @param array<mixed> $args
     */
    public function newInstance(mixed ...$args): object
    {
        if (! $this->isInstantiable()) {
            throw new \RuntimeException(
                "Cannot instantiate class '{$this->className->getFullyQualified()}': " .
                "class is not instantiable (interface, abstract class, or trait)"
            );
        }

        return $this->cache->getNativeClass($this->className)->newInstance(...$args);
    }

    /**
     * Get constructor method - delegates to native ReflectionClass
     */
    public function getConstructor(): ?\ReflectionMethod
    {
        return $this->cache->getNativeClass($this->className)->getConstructor();
    }

    /**
     * Check if method exists - delegates to native ReflectionClass
     */
    public function hasMethod(string $name): bool
    {
        return $this->cache->getNativeClass($this->className)->hasMethod($name);
    }

    /**
     * Create lazy ghost - delegates to native ReflectionClass
     */
    public function newLazyGhost(callable $initializer, int $options = 0): object
    {
        return $this->cache->getNativeClass($this->className)->newLazyGhost($initializer, $options);
    }

    /**
     * Create lazy proxy - delegates to native ReflectionClass
     */
    public function newLazyProxy(callable $factory, int $options = 0): object
    {
        return $this->cache->getNativeClass($this->className)->newLazyProxy($factory, $options);
    }

    /**
     * Get filename where the class is defined - delegates to native ReflectionClass
     */
    public function getFileName(): string
    {
        return $this->cache->getNativeClass($this->className)->getFileName();
    }

    /**
     * Get parent class - delegates to native ReflectionClass
     */
    public function getParentClass(): \ReflectionClass|false
    {
        return $this->cache->getNativeClass($this->className)->getParentClass();
    }

    /**
     * Get interface names - delegates to native ReflectionClass
     *
     * @return array<string>
     */
    public function getInterfaceNames(): array
    {
        return $this->cache->getNativeClass($this->className)->getInterfaceNames();
    }

    /**
     * Get trait names - delegates to native ReflectionClass
     *
     * @return array<string>
     */
    public function getTraitNames(): array
    {
        return $this->cache->getNativeClass($this->className)->getTraitNames();
    }

    /**
     * Check if class is abstract - delegates to native ReflectionClass
     */
    public function isAbstract(): bool
    {
        return $this->cache->getNativeClass($this->className)->isAbstract();
    }

    /**
     * Check if this is an interface - delegates to native ReflectionClass
     */
    public function isInterface(): bool
    {
        return $this->cache->getNativeClass($this->className)->isInterface();
    }

    /**
     * Check if this is a trait - delegates to native ReflectionClass
     */
    public function isTrait(): bool
    {
        return $this->cache->getNativeClass($this->className)->isTrait();
    }

    /**
     * Check if this is an enum - delegates to native ReflectionClass
     */
    public function isEnum(): bool
    {
        return $this->cache->getNativeClass($this->className)->isEnum();
    }

    /**
     * Get all class properties with visibility filter - delegates to native ReflectionClass
     *
     * @param int|null $filter
     * @return array<\ReflectionProperty>
     */
    public function getPropertiesRaw(?int $filter = null): array
    {
        return $this->cache->getNativeClass($this->className)->getProperties($filter);
    }

    /**
     * Get all class methods with visibility filter - delegates to native ReflectionClass
     *
     * @param int|null $filter
     * @return array<\ReflectionMethod>
     */
    public function getMethodsRaw(?int $filter = null): array
    {
        return $this->cache->getNativeClass($this->className)->getMethods($filter);
    }
}
