<?php

declare(strict_types=1);

namespace App\Framework\Core\ValueObjects\StructuredData;

/**
 * Structured Data Collection
 *
 * Collection of StructuredData objects for rendering multiple JSON-LD scripts.
 * Framework-compliant readonly value object with variadic constructor.
 *
 * @psalm-immutable
 */
final readonly class StructuredDataCollection implements \IteratorAggregate, \Countable
{
    /** @var array<StructuredData> */
    private array $items;

    /**
     * @param StructuredData ...$items Variadic structured data items
     */
    public function __construct(
        StructuredData ...$items
    ) {
        $this->items = $items;
    }

    /**
     * Create from array
     *
     * @param array<StructuredData> $items
     */
    public static function fromArray(array $items): self
    {
        return new self(...$items);
    }

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

    /**
     * Get iterator
     */
    public function getIterator(): \ArrayIterator
    {
        return new \ArrayIterator($this->items);
    }

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

    /**
     * Add item (immutable)
     */
    public function add(StructuredData $item): self
    {
        return new self(...[...$this->items, $item]);
    }

    /**
     * Merge with another collection (immutable)
     */
    public function merge(self $other): self
    {
        return new self(...$this->items, ...$other->items);
    }

    /**
     * Render all items as separate JSON-LD script tags
     */
    public function toScripts(): string
    {
        if ($this->isEmpty()) {
            return '';
        }

        $scripts = array_map(
            fn(StructuredData $item) => $item->toScript(),
            $this->items
        );

        return implode("\n", $scripts);
    }

    /**
     * Render all items as single JSON-LD script with array
     */
    public function toSingleScript(): string
    {
        if ($this->isEmpty()) {
            return '';
        }

        $jsonLdArray = array_map(
            fn(StructuredData $item) => $item->toJsonLd(),
            $this->items
        );

        $json = json_encode($jsonLdArray, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);

        return '<script type="application/ld+json">' . $json . '</script>';
    }

    /**
     * Convert to string (renders as separate scripts)
     */
    public function __toString(): string
    {
        return $this->toScripts();
    }
}
