<?php
declare(strict_types=1);

namespace ROITheme\Shared\Domain\ValueObjects;

use ROITheme\Shared\Domain\Exceptions\InvalidComponentException;

/**
 * ComponentName - Value Object inmutable para nombre de componente
 *
 * RESPONSABILIDAD: Representar y validar el nombre de un componente del tema
 *
 * REGLAS DE NEGOCIO:
 * - El nombre no puede estar vacío
 * - El nombre debe tener entre 3 y 50 caracteres
 * - El nombre solo puede contener letras minúsculas, números, guiones bajos y guiones
 * - El nombre debe comenzar con una letra
 *
 * INVARIANTES:
 * - Una vez creado, el nombre no puede cambiar (inmutable)
 * - El nombre siempre está en formato normalizado (lowercase, sin espacios)
 *
 * USO:
 * ```php
 * $name = new ComponentName('top_bar');
 * echo $name->value(); // "top_bar"
 * echo $name->toString(); // "top_bar"
 * ```
 *
 * @package ROITheme\Shared\Domain\ValueObjects
 */
final readonly class ComponentName
{
    /**
     * Longitud mínima del nombre
     */
    private const MIN_LENGTH = 3;

    /**
     * Longitud máxima del nombre
     */
    private const MAX_LENGTH = 50;

    /**
     * Patrón regex para validar formato del nombre
     * - Debe comenzar con letra minúscula
     * - Puede contener letras minúsculas, números, guiones bajos y guiones
     */
    private const PATTERN = '/^[a-z][a-z0-9_-]*$/';

    /**
     * @param string $value Nombre del componente
     * @throws InvalidComponentException Si el nombre no cumple las reglas de negocio
     */
    public function __construct(private string $value)
    {
        $this->validate();
    }

    /**
     * Obtener el valor del nombre
     *
     * @return string
     */
    public function value(): string
    {
        return $this->value;
    }

    /**
     * Convertir a string
     *
     * @return string
     */
    public function toString(): string
    {
        return $this->value;
    }

    /**
     * Comparar con otro ComponentName
     *
     * @param ComponentName $other
     * @return bool
     */
    public function equals(ComponentName $other): bool
    {
        return $this->value === $other->value;
    }

    /**
     * Validar reglas de negocio del nombre
     *
     * @throws InvalidComponentException
     * @return void
     */
    private function validate(): void
    {
        // Regla 1: No puede estar vacío
        if (empty($this->value)) {
            throw new InvalidComponentException('Component name cannot be empty');
        }

        // Regla 2: Longitud entre MIN y MAX
        $length = strlen($this->value);
        if ($length < self::MIN_LENGTH || $length > self::MAX_LENGTH) {
            throw new InvalidComponentException(
                sprintf(
                    'Component name must be between %d and %d characters (got %d)',
                    self::MIN_LENGTH,
                    self::MAX_LENGTH,
                    $length
                )
            );
        }

        // Regla 3: Formato válido (lowercase, números, guiones bajos, comienza con letra)
        if (!preg_match(self::PATTERN, $this->value)) {
            throw new InvalidComponentException(
                'Component name must start with a letter and contain only lowercase letters, numbers, underscores, and hyphens'
            );
        }
    }

    /**
     * Crear desde string (factory method)
     *
     * @param string $value
     * @return self
     */
    public static function fromString(string $value): self
    {
        return new self($value);
    }

    /**
     * Normalizar string a formato de nombre válido
     *
     * Convierte:
     * - "Top Bar" → "top_bar"
     * - "FOOTER-CTA" → "footer_cta"
     * - "  sidebar  " → "sidebar"
     *
     * @param string $value
     * @return self
     */
    public static function fromNormalized(string $value): self
    {
        // Trim espacios
        $normalized = trim($value);

        // Convertir a minúsculas
        $normalized = strtolower($normalized);

        // Reemplazar espacios y guiones por guiones bajos
        $normalized = str_replace([' ', '-'], '_', $normalized);

        // Eliminar caracteres no permitidos
        $normalized = preg_replace('/[^a-z0-9_]/', '', $normalized);

        // Eliminar guiones bajos duplicados
        $normalized = preg_replace('/_+/', '_', $normalized);

        // Eliminar guiones bajos al inicio/final
        $normalized = trim($normalized, '_');

        return new self($normalized);
    }
}
