<?php

declare(strict_types=1);

namespace App\Framework\Reflection;

use App\Framework\Core\ValueObjects\ClassName;
use App\Framework\Reflection\Cache\AttributeCache;
use Attribute;

final readonly class WrappedReflectionAttribute
{
    public function __construct(
        private ClassName $className,
        private ?string $methodName,
        private string $attributeClass,
        private int $index,
        private AttributeCache $cache
    ) {
    }

    public function getName(): string
    {
        // BUGFIX: Get the attribute name directly from the native attributes array using the correct index
        $nativeAttributes = $this->methodName
            ? $this->cache->getNativeMethodAttributes($this->className, $this->methodName, null)
            : $this->cache->getNativeClassAttributes($this->className, null);

        return isset($nativeAttributes[$this->index]) ? $nativeAttributes[$this->index]->getName() : '';
    }

    public function getShortName(): string
    {
        return $this->cache->getAttributeShortName($this->className, $this->methodName, $this->attributeClass, $this->index);
    }

    public function getTarget(): int
    {
        return $this->cache->getAttributeTarget($this->className, $this->methodName, $this->attributeClass, $this->index);
    }

    public function isRepeated(): bool
    {
        return $this->cache->isAttributeRepeated($this->className, $this->methodName, $this->attributeClass, $this->index);
    }

    /**
     * @return array<mixed>
     */
    public function getArguments(): array
    {
        return $this->cache->getAttributeArguments($this->className, $this->methodName, $this->attributeClass, $this->index);
    }

    public function newInstance(): ?object
    {
        return $this->cache->getAttributeInstance($this->className, $this->methodName, $this->attributeClass, $this->index);
    }

    public function hasArguments(): bool
    {
        return ! empty($this->getArguments());
    }

    public function getArgumentCount(): int
    {
        return count($this->getArguments());
    }

    public function getArgument(int $index): mixed
    {
        $args = $this->getArguments();

        return $args[$index] ?? null;
    }

    public function getFirstArgument(): mixed
    {
        return $this->getArgument(0);
    }

    /**
     * Check if attribute targets class
     */
    public function targetsClass(): bool
    {
        return ($this->getTarget() & Attribute::TARGET_CLASS) !== 0;
    }

    /**
     * Check if attribute targets method
     */
    public function targetsMethod(): bool
    {
        return ($this->getTarget() & Attribute::TARGET_METHOD) !== 0;
    }

    /**
     * Check if attribute targets property
     */
    public function targetsProperty(): bool
    {
        return ($this->getTarget() & Attribute::TARGET_PROPERTY) !== 0;
    }

    /**
     * Check if attribute targets parameter
     */
    public function targetsParameter(): bool
    {
        return ($this->getTarget() & Attribute::TARGET_PARAMETER) !== 0;
    }

    /**
     * Check if attribute targets function
     */
    public function targetsFunction(): bool
    {
        return ($this->getTarget() & Attribute::TARGET_FUNCTION) !== 0;
    }

    /**
     * Check if attribute targets constant
     */
    public function targetsConstant(): bool
    {
        return ($this->getTarget() & Attribute::TARGET_CLASS_CONSTANT) !== 0;
    }

    /**
     * Check if attribute targets all
     */
    public function targetsAll(): bool
    {
        return ($this->getTarget() & Attribute::TARGET_ALL) !== 0;
    }

    /**
     * Get instance with type checking
     */
    public function getInstanceOf(string $expectedClass): ?object
    {
        $instance = $this->newInstance();

        return $instance instanceof $expectedClass ? $instance : null;
    }

    /**
     * Check if instance is of specific type
     */
    public function isInstanceOf(string $className): bool
    {
        return $this->getName() === $className || is_subclass_of($this->getName(), $className);
    }

    /**
     * Convert to array for debugging
     * @return array<string, mixed>
     */
    public function toArray(): array
    {
        return [
            'name' => $this->getName(),
            'shortName' => $this->getShortName(),
            'target' => $this->getTarget(),
            'isRepeated' => $this->isRepeated(),
            'arguments' => $this->getArguments(),
            'argumentCount' => $this->getArgumentCount(),
        ];
    }
}
