<?php

declare(strict_types=1);

namespace App\Framework\LiveComponents\ValueObjects;

/**
 * Component Fragment Value Object
 *
 * Represents a named fragment within a LiveComponent template.
 * Fragments enable partial rendering - updating only specific parts
 * of a component instead of re-rendering the entire component.
 *
 * Template Convention:
 * ```html
 * <div data-lc-fragment="user-stats">
 *   <h3>User Statistics</h3>
 *   <p>Total: {stats.total}</p>
 * </div>
 * ```
 *
 * Usage in action:
 * ```php
 * #[Action(fragments: ['user-stats'])]
 * public function refreshStats(): ComponentData
 * {
 *     $this->stats = $this->calculateStats();
 *     return $this->getData();
 * }
 * ```
 */
final readonly class ComponentFragment
{
    public function __construct(
        public string $name,
        public string $html
    ) {
        $this->validate();
    }

    /**
     * Create fragment from name and HTML
     */
    public static function create(string $name, string $html): self
    {
        return new self($name, $html);
    }

    /**
     * Validate fragment
     */
    private function validate(): void
    {
        if (empty($this->name)) {
            throw new \InvalidArgumentException('Fragment name cannot be empty');
        }

        // Fragment names should be alphanumeric with hyphens/underscores
        if (! preg_match('/^[a-z0-9_-]+$/i', $this->name)) {
            throw new \InvalidArgumentException(
                "Invalid fragment name '{$this->name}'. " .
                "Only alphanumeric characters, hyphens, and underscores are allowed."
            );
        }
    }

    /**
     * Convert to array for JSON serialization
     */
    public function toArray(): array
    {
        return [
            'name' => $this->name,
            'html' => $this->html,
        ];
    }

    /**
     * Check if fragment equals another fragment
     */
    public function equals(self $other): bool
    {
        return $this->name === $other->name
            && $this->html === $other->html;
    }

    /**
     * Get fragment size in bytes
     */
    public function size(): int
    {
        return strlen($this->html);
    }

    /**
     * Check if fragment is empty
     */
    public function isEmpty(): bool
    {
        return empty(trim($this->html));
    }
}
