<?php

declare(strict_types=1);

namespace App\Framework\Core\ValueObjects;

/**
 * Value object representing a fully qualified method reference (ClassName::methodName)
 */
final readonly class QualifiedMethodName
{
    public function __construct(
        public ClassName $className,
        public MethodName $methodName
    ) {}

    /**
     * Create from qualified string "ClassName::methodName"
     */
    public static function fromString(string $qualifiedMethod): self
    {
        return MethodName::fromQualified($qualifiedMethod);
    }

    /**
     * Check if method exists
     */
    public function exists(): bool
    {
        return $this->methodName->existsIn($this->className);
    }

    /**
     * Check if method is public
     */
    public function isPublic(): bool
    {
        return $this->methodName->isPublicIn($this->className);
    }

    /**
     * Check if method is static
     */
    public function isStatic(): bool
    {
        return $this->methodName->isStaticIn($this->className);
    }

    /**
     * Get reflection for this method
     */
    public function getReflection(): ?\ReflectionMethod
    {
        return $this->methodName->getReflection($this->className);
    }

    /**
     * Check if this is a magic method
     */
    public function isMagicMethod(): bool
    {
        return $this->methodName->isMagicMethod();
    }

    /**
     * Check if this is a constructor
     */
    public function isConstructor(): bool
    {
        return $this->methodName->isConstructor();
    }

    /**
     * Check if this is __invoke
     */
    public function isInvokable(): bool
    {
        return $this->methodName->isInvokable();
    }

    /**
     * Get string representation "ClassName::methodName"
     */
    public function toString(): string
    {
        return $this->className->toString() . '::' . $this->methodName->toString();
    }

    /**
     * Get string representation with namespace
     */
    public function toFullyQualifiedString(): string
    {
        return $this->className->getFullyQualified() . '::' . $this->methodName->toString();
    }

    /**
     * Compare for equality
     */
    public function equals(self $other): bool
    {
        return $this->className->equals($other->className)
            && $this->methodName->equals($other->methodName);
    }

    /**
     * Invoke the method with arguments
     *
     * @param object|null $instance Instance for non-static methods, null for static
     * @param array $arguments Method arguments
     * @return mixed
     */
    public function invoke(?object $instance, array $arguments = []): mixed
    {
        if (! $this->exists()) {
            throw new \BadMethodCallException(
                "Method {$this->toString()} does not exist"
            );
        }

        $reflection = $this->getReflection();

        if ($reflection->isStatic()) {
            return $reflection->invokeArgs(null, $arguments);
        }

        if ($instance === null) {
            throw new \BadMethodCallException(
                "Cannot invoke non-static method {$this->toString()} without instance"
            );
        }

        return $reflection->invokeArgs($instance, $arguments);
    }

    /**
     * String representation
     */
    public function __toString(): string
    {
        return $this->toString();
    }
}
