<?php

declare(strict_types=1);

namespace App\Framework\LiveComponents\ValueObjects;

/**
 * Slot Definition Value Object
 *
 * Defines a slot that can be filled with content from parent components.
 * Similar to Vue's slots or React's children props.
 *
 * Example:
 * ```php
 * $slot = SlotDefinition::create(
 *     name: 'header',
 *     defaultContent: '<h3>Default Header</h3>',
 *     props: ['title', 'subtitle']
 * );
 * ```
 */
final readonly class SlotDefinition
{
    /**
     * @param string $name Slot name (e.g., 'default', 'header', 'footer')
     * @param string $defaultContent Fallback content if slot is not filled
     * @param array<string> $props Props that this slot accepts from parent
     * @param bool $required Whether this slot must be filled
     */
    public function __construct(
        public string $name,
        public string $defaultContent = '',
        public array $props = [],
        public bool $required = false
    ) {
        if (empty($name)) {
            throw new \InvalidArgumentException('Slot name cannot be empty');
        }
    }

    /**
     * Create a default slot (unnamed)
     */
    public static function default(string $defaultContent = ''): self
    {
        return new self(
            name: 'default',
            defaultContent: $defaultContent,
            props: [],
            required: false
        );
    }

    /**
     * Create a named slot
     */
    public static function named(string $name, string $defaultContent = ''): self
    {
        return new self(
            name: $name,
            defaultContent: $defaultContent,
            props: [],
            required: false
        );
    }

    /**
     * Create a scoped slot (with props)
     */
    public static function scoped(string $name, array $props, string $defaultContent = ''): self
    {
        return new self(
            name: $name,
            defaultContent: $defaultContent,
            props: $props,
            required: false
        );
    }

    /**
     * Mark slot as required
     */
    public function withRequired(bool $required = true): self
    {
        return new self(
            name: $this->name,
            defaultContent: $this->defaultContent,
            props: $this->props,
            required: $required
        );
    }

    /**
     * Set default content
     */
    public function withDefaultContent(string $content): self
    {
        return new self(
            name: $this->name,
            defaultContent: $content,
            props: $this->props,
            required: $this->required
        );
    }

    /**
     * Add props that this slot accepts
     */
    public function withProps(array $props): self
    {
        return new self(
            name: $this->name,
            defaultContent: $this->defaultContent,
            props: $props,
            required: $this->required
        );
    }

    /**
     * Check if slot is the default slot
     */
    public function isDefault(): bool
    {
        return $this->name === 'default';
    }

    /**
     * Check if slot is scoped (has props)
     */
    public function isScoped(): bool
    {
        return ! empty($this->props);
    }

    /**
     * Check if slot has default content
     */
    public function hasDefaultContent(): bool
    {
        return ! empty($this->defaultContent);
    }

    /**
     * Convert to array for serialization
     */
    public function toArray(): array
    {
        return [
            'name' => $this->name,
            'default_content' => $this->defaultContent,
            'props' => $this->props,
            'required' => $this->required,
            'is_default' => $this->isDefault(),
            'is_scoped' => $this->isScoped(),
        ];
    }

    /**
     * Check equality with another slot definition
     */
    public function equals(self $other): bool
    {
        return $this->name === $other->name
            && $this->defaultContent === $other->defaultContent
            && $this->props === $other->props
            && $this->required === $other->required;
    }
}
