<?php

declare(strict_types=1);

namespace App\Framework\LiveComponents\Batch;

/**
 * Batch Request Value Object
 *
 * Represents a batch of operations to execute.
 *
 * Example:
 * {
 *   "operations": [
 *     {
 *       "componentId": "counter:demo",
 *       "method": "increment",
 *       "params": {"amount": 5}
 *     },
 *     {
 *       "componentId": "user-stats:123",
 *       "method": "refresh",
 *       "fragments": ["stats-display"]
 *     }
 *   ]
 * }
 */
final readonly class BatchRequest
{
    /** @var array<BatchOperation> */
    public array $operations;

    /**
     * @param BatchOperation ...$operations - Variadic parameter for type safety
     */
    public function __construct(BatchOperation ...$operations)
    {
        $this->operations = $operations;
        $this->validate();
    }

    /**
     * Create from array (from JSON request)
     */
    public static function fromArray(array $data): self
    {
        if (! isset($data['operations']) || ! is_array($data['operations'])) {
            throw new \InvalidArgumentException('BatchRequest: operations array is required');
        }

        $operations = array_map(
            fn (array $op) => BatchOperation::fromArray($op),
            $data['operations']
        );

        return new self(...$operations);
    }

    /**
     * Validate batch request
     */
    private function validate(): void
    {
        if (empty($this->operations)) {
            throw new \InvalidArgumentException('BatchRequest: at least one operation is required');
        }

        // Enforce max batch size
        $maxBatchSize = 50; // Configurable limit
        if (count($this->operations) > $maxBatchSize) {
            throw new \InvalidArgumentException(
                "BatchRequest: batch size exceeds maximum ({$maxBatchSize} operations)"
            );
        }
    }

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

    /**
     * Get operation by index
     */
    public function getOperation(int $index): ?BatchOperation
    {
        return $this->operations[$index] ?? null;
    }

    /**
     * Get all operations
     *
     * @return array<BatchOperation>
     */
    public function getOperations(): array
    {
        return $this->operations;
    }

    /**
     * Convert to array for debugging
     */
    public function toArray(): array
    {
        return [
            'operations' => array_map(
                fn (BatchOperation $op) => $op->toArray(),
                $this->operations
            ),
        ];
    }
}
