<?php
declare(strict_types=1);

namespace ROITheme\Shared\Domain\ValueObjects;

use ROITheme\Shared\Domain\Exceptions\InvalidComponentException;

/**
 * ComponentContent - Value Object inmutable para contenido de componente
 *
 * RESPONSABILIDAD: Representar y validar el contenido textual y de CTA de un componente
 *
 * REGLAS DE NEGOCIO:
 * - El mensaje de texto puede estar vacío (componentes sin texto)
 * - Si existe CTA URL debe existir CTA text (y viceversa)
 * - CTA URL debe ser una URL válida (formato http/https)
 * - CTA text no puede estar vacío si existe CTA URL
 * - Title es opcional
 *
 * CASOS DE USO:
 * - Componente solo con mensaje de texto
 * - Componente con mensaje y CTA
 * - Componente con title, mensaje y CTA
 * - Componente sin contenido textual (solo estilos/visibilidad)
 *
 * USO:
 * ```php
 * // Componente con mensaje y CTA
 * $content = new ComponentContent(
 *     messageText: 'Welcome to our site!',
 *     ctaText: 'Learn More',
 *     ctaUrl: 'https://example.com/about'
 * );
 *
 * // Componente solo con mensaje
 * $content = ComponentContent::messageOnly('Hello World!');
 *
 * // Componente vacío
 * $content = ComponentContent::empty();
 * ```
 *
 * @package ROITheme\Shared\Domain\ValueObjects
 */
final readonly class ComponentContent
{
    /**
     * Constructor
     *
     * @param string $messageText Texto del mensaje principal
     * @param string|null $ctaText Texto del Call-to-Action
     * @param string|null $ctaUrl URL del Call-to-Action
     * @param string|null $title Título del componente
     * @throws InvalidComponentException
     */
    public function __construct(
        private string $messageText = '',
        private ?string $ctaText = null,
        private ?string $ctaUrl = null,
        private ?string $title = null
    ) {
        $this->validate();
    }

    /**
     * Obtener mensaje de texto
     *
     * @return string
     */
    public function messageText(): string
    {
        return $this->messageText;
    }

    /**
     * Obtener texto del CTA
     *
     * @return string|null
     */
    public function ctaText(): ?string
    {
        return $this->ctaText;
    }

    /**
     * Obtener URL del CTA
     *
     * @return string|null
     */
    public function ctaUrl(): ?string
    {
        return $this->ctaUrl;
    }

    /**
     * Obtener título
     *
     * @return string|null
     */
    public function title(): ?string
    {
        return $this->title;
    }

    /**
     * Verificar si tiene mensaje de texto
     *
     * @return bool
     */
    public function hasMessage(): bool
    {
        return !empty($this->messageText);
    }

    /**
     * Verificar si tiene CTA
     *
     * @return bool
     */
    public function hasCTA(): bool
    {
        return !empty($this->ctaText) && !empty($this->ctaUrl);
    }

    /**
     * Verificar si tiene título
     *
     * @return bool
     */
    public function hasTitle(): bool
    {
        return !empty($this->title);
    }

    /**
     * Verificar si está completamente vacío
     *
     * @return bool
     */
    public function isEmpty(): bool
    {
        return empty($this->messageText) &&
            empty($this->ctaText) &&
            empty($this->ctaUrl) &&
            empty($this->title);
    }

    /**
     * Convertir a array
     *
     * @return array
     */
    public function toArray(): array
    {
        return array_filter([
            'message_text' => $this->messageText,
            'cta_text' => $this->ctaText,
            'cta_url' => $this->ctaUrl,
            'title' => $this->title
        ], fn($value) => $value !== null && $value !== '');
    }

    /**
     * Validar reglas de negocio
     *
     * @throws InvalidComponentException
     * @return void
     */
    private function validate(): void
    {
        // Regla 1: Si existe CTA URL debe existir CTA text
        if (!empty($this->ctaUrl) && empty($this->ctaText)) {
            throw new InvalidComponentException('CTA URL requires CTA text');
        }

        // Regla 2: Si existe CTA text debe existir CTA URL
        if (!empty($this->ctaText) && empty($this->ctaUrl)) {
            throw new InvalidComponentException('CTA text requires CTA URL');
        }

        // Regla 3: CTA URL debe ser URL válida
        if (!empty($this->ctaUrl) && !$this->isValidUrl($this->ctaUrl)) {
            throw new InvalidComponentException(
                sprintf('Invalid CTA URL format: %s', $this->ctaUrl)
            );
        }
    }

    /**
     * Validar si una string es URL válida
     *
     * @param string $url
     * @return bool
     */
    private function isValidUrl(string $url): bool
    {
        // Debe comenzar con http:// o https://
        if (!preg_match('/^https?:\/\//i', $url)) {
            return false;
        }

        // Usar filter_var para validación completa
        return filter_var($url, FILTER_VALIDATE_URL) !== false;
    }

    /**
     * Factory: Contenido vacío
     *
     * @return self
     */
    public static function empty(): self
    {
        return new self();
    }

    /**
     * Factory: Solo mensaje
     *
     * @param string $message
     * @return self
     */
    public static function messageOnly(string $message): self
    {
        return new self(messageText: $message);
    }

    /**
     * Factory: Mensaje con CTA
     *
     * @param string $message
     * @param string $ctaText
     * @param string $ctaUrl
     * @return self
     */
    public static function withCTA(string $message, string $ctaText, string $ctaUrl): self
    {
        return new self(
            messageText: $message,
            ctaText: $ctaText,
            ctaUrl: $ctaUrl
        );
    }

    /**
     * Crear desde array
     *
     * @param array $data
     * @return self
     */
    public static function fromArray(array $data): self
    {
        return new self(
            messageText: $data['message_text'] ?? '',
            ctaText: $data['cta_text'] ?? null,
            ctaUrl: $data['cta_url'] ?? null,
            title: $data['title'] ?? null
        );
    }

    /**
     * Comparar con otro ComponentContent
     *
     * @param ComponentContent $other
     * @return bool
     */
    public function equals(ComponentContent $other): bool
    {
        return $this->messageText === $other->messageText &&
            $this->ctaText === $other->ctaText &&
            $this->ctaUrl === $other->ctaUrl &&
            $this->title === $other->title;
    }
}
