<?php

declare(strict_types=1);

namespace App\Framework\LiveComponents\ValueObjects;

/**
 * Slot Context Value Object
 *
 * Provides context/data to scoped slots.
 * Allows parent components to pass data to slot content.
 *
 * Example:
 * ```php
 * $context = SlotContext::create([
 *     'items' => $items,
 *     'total' => count($items),
 *     'hasMore' => $hasMore
 * ]);
 * ```
 */
final readonly class SlotContext
{
    /**
     * @param array $data Context data available to slots
     * @param array $metadata Additional metadata about the context
     */
    public function __construct(
        public array $data = [],
        public array $metadata = []
    ) {
    }

    /**
     * Create empty context
     */
    public static function empty(): self
    {
        return new self([], []);
    }

    /**
     * Create context with data
     */
    public static function create(array $data, array $metadata = []): self
    {
        return new self($data, $metadata);
    }

    /**
     * Get data value by key
     */
    public function get(string $key, mixed $default = null): mixed
    {
        return $this->data[$key] ?? $default;
    }

    /**
     * Check if key exists in data
     */
    public function has(string $key): bool
    {
        return array_key_exists($key, $this->data);
    }

    /**
     * Get metadata value
     */
    public function getMetadata(string $key, mixed $default = null): mixed
    {
        return $this->metadata[$key] ?? $default;
    }

    /**
     * Add data to context
     */
    public function withData(array $data): self
    {
        return new self(
            data: array_merge($this->data, $data),
            metadata: $this->metadata
        );
    }

    /**
     * Add metadata to context
     */
    public function withMetadata(array $metadata): self
    {
        return new self(
            data: $this->data,
            metadata: array_merge($this->metadata, $metadata)
        );
    }

    /**
     * Set a single data value
     */
    public function with(string $key, mixed $value): self
    {
        return new self(
            data: array_merge($this->data, [$key => $value]),
            metadata: $this->metadata
        );
    }

    /**
     * Remove a data key
     */
    public function without(string $key): self
    {
        $data = $this->data;
        unset($data[$key]);

        return new self(
            data: $data,
            metadata: $this->metadata
        );
    }

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

    /**
     * Get all data keys
     */
    public function keys(): array
    {
        return array_keys($this->data);
    }

    /**
     * Get data count
     */
    public function count(): int
    {
        return count($this->data);
    }

    /**
     * Merge with another context
     */
    public function merge(self $other): self
    {
        return new self(
            data: array_merge($this->data, $other->data),
            metadata: array_merge($this->metadata, $other->metadata)
        );
    }

    /**
     * Convert to array for serialization
     */
    public function toArray(): array
    {
        return [
            'data' => $this->data,
            'metadata' => $this->metadata,
            'is_empty' => $this->isEmpty(),
            'keys' => $this->keys(),
            'count' => $this->count(),
        ];
    }

    /**
     * Create from array
     */
    public static function fromArray(array $array): self
    {
        return new self(
            data: $array['data'] ?? [],
            metadata: $array['metadata'] ?? []
        );
    }

    /**
     * Convert to plain array (only data, no metadata)
     */
    public function toPlainArray(): array
    {
        return $this->data;
    }
}
