<?php

declare(strict_types=1);

/**
 * This code was auto-generated by {this script}[https://github.com/cucumber/messages/blob/main/jsonschema/scripts/codegen.rb]
 */

namespace Cucumber\Messages;

use JsonSerializable;
use Cucumber\Messages\DecodingException\SchemaViolationException;

/**
 * Represents the GherkinDocument message in Cucumber's message protocol
 * @see https://github.com/cucumber/messages
 *
 * The [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) of a Gherkin document.
 * Cucumber implementations should *not* depend on `GherkinDocument` or any of its
 * children for execution - use [Pickle](#io.cucumber.messages.Pickle) instead.
 *
 * The only consumers of `GherkinDocument` should only be formatters that produce
 * "rich" output, resembling the original Gherkin document. */
final class GherkinDocument implements JsonSerializable
{
    use JsonEncodingTrait;

    /**
     * Construct the GherkinDocument with all properties
     *
     * @param list<Comment> $comments
     */
    public function __construct(

        /**
         * The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier)
         * of the source, typically a file path relative to the root directory
         */
        public readonly ?string $uri = null,
        public readonly ?Feature $feature = null,

        /**
         * All the comments in the Gherkin document
         */
        public readonly array $comments = [],
    ) {
    }

    /**
     * @throws SchemaViolationException
     *
     * @internal
     */
    public static function fromArray(array $arr): self
    {
        self::ensureUri($arr);
        self::ensureFeature($arr);
        self::ensureComments($arr);

        return new self(
            isset($arr['uri']) ? (string) $arr['uri'] : null,
            isset($arr['feature']) ? Feature::fromArray($arr['feature']) : null,
            array_values(array_map(fn (array $member) => Comment::fromArray($member), $arr['comments'])),
        );
    }

    /**
     * @psalm-assert array{uri?: string|int|bool} $arr
     */
    private static function ensureUri(array $arr): void
    {
        if (array_key_exists('uri', $arr) && is_array($arr['uri'])) {
            throw new SchemaViolationException('Property \'uri\' was array');
        }
    }

    /**
     * @psalm-assert array{feature?: array} $arr
     */
    private static function ensureFeature(array $arr): void
    {
        if (array_key_exists('feature', $arr) && !is_array($arr['feature'])) {
            throw new SchemaViolationException('Property \'feature\' was not array');
        }
    }

    /**
     * @psalm-assert array{comments: array} $arr
     */
    private static function ensureComments(array $arr): void
    {
        if (!array_key_exists('comments', $arr)) {
            throw new SchemaViolationException('Property \'comments\' is required but was not found');
        }
        if (array_key_exists('comments', $arr) && !is_array($arr['comments'])) {
            throw new SchemaViolationException('Property \'comments\' was not array');
        }
    }
}
