<?php

declare(strict_types=1);

namespace App\Framework\Router\ValueObjects;

use App\Framework\TypeCaster\TypeCasterRegistry;
use InvalidArgumentException;

/**
 * Placeholder that automatically casts to Value Objects using TypeCasters
 * Composition-based approach without inheritance
 */
final readonly class TypedPlaceholder
{
    private function __construct(
        private Placeholder $placeholder,
        private string $valueObjectClass,
        private TypeCasterRegistry $typeCasterRegistry
    ) {
        // Validate that we have a caster for this type
        if ($this->typeCasterRegistry->getCasterForType($this->valueObjectClass) === null) {
            throw new InvalidArgumentException(
                "No TypeCaster found for type: {$this->valueObjectClass}"
            );
        }
    }

    /**
     * Create typed placeholder for automatic Value Object binding
     */
    public static function for(
        string $valueObjectClass,
        TypeCasterRegistry $typeCasterRegistry,
        ?string $name = null
    ): self {
        // Auto-generate name from class if not provided
        if ($name === null) {
            $name = self::generateNameFromClass($valueObjectClass);
        }

        // Get pattern from TypeCaster
        $pattern = $typeCasterRegistry->getPattern($valueObjectClass);

        $placeholder = Placeholder::typed($name, 'custom', $pattern);

        return new self($placeholder, $valueObjectClass, $typeCasterRegistry);
    }

    /**
     * Get the underlying placeholder
     */
    public function getPlaceholder(): Placeholder
    {
        return $this->placeholder;
    }

    /**
     * Get the Value Object class for conversion
     */
    public function getValueObjectClass(): string
    {
        return $this->valueObjectClass;
    }

    /**
     * Convert raw route value to Value Object instance
     */
    public function convertValue(string $rawValue): mixed
    {
        return $this->typeCasterRegistry->fromString($rawValue, $this->valueObjectClass);
    }

    /**
     * Get placeholder name
     */
    public function getName(): string
    {
        return $this->placeholder->getName();
    }

    /**
     * Get regex pattern
     */
    public function getPattern(): string
    {
        return $this->placeholder->getPattern();
    }

    /**
     * Convert to string representation for route compilation
     */
    public function toString(): string
    {
        return $this->placeholder->toString();
    }

    /**
     * Generate parameter name from class name
     */
    private static function generateNameFromClass(string $className): string
    {
        $shortName = basename(str_replace('\\', '/', $className));

        // Convert UserId -> userId, ProductSlug -> productSlug
        return lcfirst($shortName);
    }

    /**
     * Convert to string when used as string
     */
    public function __toString(): string
    {
        return $this->toString();
    }
}
