<?php

declare(strict_types=1);

namespace App\Framework\DI\ValueObjects;

use App\Framework\Core\ValueObjects\ClassName;
use App\Framework\Core\ValueObjects\MethodName;

/**
 * Represents a node in the dependency graph for Initializer processing
 *
 * Each node contains information about a service initializer including
 * the class that provides it, the method to call, and dependencies.
 */
final readonly class DependencyGraphNode
{
    /**
     * @param string $returnType The type that this initializer returns/provides
     * @param ClassName $className The class containing the initializer method
     * @param MethodName $methodName The method name to invoke
     * @param string[] $dependencies Array of return types this node depends on
     */
    public function __construct(
        public string $returnType,
        public ClassName $className,
        public MethodName $methodName,
        public array $dependencies = []
    ) {
    }

    /**
     * Create a node from legacy array format
     *
     * @param string $returnType
     * @param array{class: string, method: string, dependencies?: string[]} $nodeData
     * @return self
     */
    public static function fromArray(string $returnType, array $nodeData): self
    {
        return new self(
            returnType: $returnType,
            className: ClassName::create($nodeData['class']),
            methodName: MethodName::create($nodeData['method']),
            dependencies: $nodeData['dependencies'] ?? []
        );
    }

    /**
     * Convert to legacy array format for backward compatibility
     *
     * @return array{class: string, method: string, dependencies: string[]}
     */
    public function toArray(): array
    {
        return [
            'class' => $this->className->getFullyQualified(),
            'method' => $this->methodName->toString(),
            'dependencies' => $this->dependencies,
        ];
    }

    /**
     * Check if this node has any dependencies
     */
    public function hasDependencies(): bool
    {
        return ! empty($this->dependencies);
    }

    /**
     * Check if this node depends on a specific return type
     */
    public function dependsOn(string $returnType): bool
    {
        return in_array($returnType, $this->dependencies, true);
    }

    /**
     * Get a new node with additional dependencies
     *
     * @param string[] $additionalDependencies
     */
    public function withDependencies(array $additionalDependencies): self
    {
        return new self(
            returnType: $this->returnType,
            className: $this->className,
            methodName: $this->methodName,
            dependencies: array_unique(array_merge($this->dependencies, $additionalDependencies))
        );
    }

    /**
     * Get the fully qualified class name as string
     */
    public function getClassName(): string
    {
        return $this->className->getFullyQualified();
    }

    /**
     * Get the method name as string
     */
    public function getMethodName(): string
    {
        return $this->methodName->toString();
    }

    /**
     * Get a human-readable string representation
     */
    public function toString(): string
    {
        $deps = empty($this->dependencies)
            ? 'no dependencies'
            : 'depends on: ' . implode(', ', $this->dependencies);

        return "{$this->returnType} -> {$this->getClassName()}::{$this->getMethodName()} ({$deps})";
    }

    /**
     * Get debug information
     *
     * @return array{return_type: string, class: string, method: string, dependencies: string[], has_dependencies: bool}
     */
    public function getDebugInfo(): array
    {
        return [
            'return_type' => $this->returnType,
            'class' => $this->getClassName(),
            'method' => $this->getMethodName(),
            'dependencies' => $this->dependencies,
            'has_dependencies' => $this->hasDependencies(),
        ];
    }
}
