<?php

declare(strict_types=1);

namespace App\Framework\Discovery\ValueObjects;

use App\Framework\Core\ValueObjects\ClassName;

final readonly class DependencyEdge
{
    public function __construct(
        private ClassName $source,
        private ClassName $target,
        private DependencyRelation $relation,
        private int $weight = 1
    ) {
    }

    /**
     * Create a dependency edge
     */
    public static function create(
        ClassName $source,
        ClassName $target,
        DependencyRelation $relation,
        int $weight = 1
    ): self {
        return new self($source, $target, $relation, $weight);
    }

    /**
     * Get the source class (the one that depends on target)
     */
    public function getSource(): ClassName
    {
        return $this->source;
    }

    /**
     * Get the target class (the dependency)
     */
    public function getTarget(): ClassName
    {
        return $this->target;
    }

    /**
     * Get the type of dependency relationship
     */
    public function getRelation(): DependencyRelation
    {
        return $this->relation;
    }

    /**
     * Get the weight/importance of this dependency
     */
    public function getWeight(): int
    {
        return $this->weight;
    }

    /**
     * Check if this is a circular dependency (source == target)
     */
    public function isCircular(): bool
    {
        return $this->source->equals($this->target);
    }

    /**
     * Check if this is a strong dependency (constructor injection)
     */
    public function isStrong(): bool
    {
        return $this->relation === DependencyRelation::CONSTRUCTOR_INJECTION;
    }

    /**
     * Check if this is a weak dependency (method parameter, property)
     */
    public function isWeak(): bool
    {
        return in_array($this->relation, [
            DependencyRelation::METHOD_PARAMETER,
            DependencyRelation::PROPERTY_TYPE,
            DependencyRelation::RETURN_TYPE,
        ]);
    }

    /**
     * Create an inverted edge (target -> source)
     */
    public function invert(): self
    {
        return new self(
            $this->target,
            $this->source,
            $this->relation->invert(),
            $this->weight
        );
    }

    /**
     * Convert to array for serialization
     *
     * @return array<string, mixed>
     */
    public function toArray(): array
    {
        return [
            'source' => $this->source->toString(),
            'target' => $this->target->toString(),
            'relation' => $this->relation->value,
            'weight' => $this->weight,
            'is_circular' => $this->isCircular(),
            'is_strong' => $this->isStrong(),
            'is_weak' => $this->isWeak(),
        ];
    }

    /**
     * Convert to string representation
     */
    public function toString(): string
    {
        $arrow = $this->isStrong() ? ' => ' : ' -> ';

        return sprintf(
            '%s%s%s (%s, weight: %d)',
            $this->source->getShortName(),
            $arrow,
            $this->target->getShortName(),
            $this->relation->value,
            $this->weight
        );
    }

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