<?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 Envelope message in Cucumber's message protocol
 * @see https://github.com/cucumber/messages
 *
 * When removing a field, replace it with reserved, rather than deleting the line.
 * When adding a field, add it to the end and increment the number by one.
 * See https://developers.google.com/protocol-buffers/docs/proto#updating for details
 *
 * All the messages that are passed between different components/processes are Envelope
 * messages. */
final class Envelope implements JsonSerializable
{
    use JsonEncodingTrait;

    /**
     * Construct the Envelope with all properties
     *
     */
    public function __construct(
        public readonly ?Attachment $attachment = null,
        public readonly ?GherkinDocument $gherkinDocument = null,
        public readonly ?Hook $hook = null,
        public readonly ?Meta $meta = null,
        public readonly ?ParameterType $parameterType = null,
        public readonly ?ParseError $parseError = null,
        public readonly ?Pickle $pickle = null,
        public readonly ?Source $source = null,
        public readonly ?StepDefinition $stepDefinition = null,
        public readonly ?TestCase $testCase = null,
        public readonly ?TestCaseFinished $testCaseFinished = null,
        public readonly ?TestCaseStarted $testCaseStarted = null,
        public readonly ?TestRunFinished $testRunFinished = null,
        public readonly ?TestRunStarted $testRunStarted = null,
        public readonly ?TestStepFinished $testStepFinished = null,
        public readonly ?TestStepStarted $testStepStarted = null,
        public readonly ?UndefinedParameterType $undefinedParameterType = null,
    ) {
    }

    /**
     * @throws SchemaViolationException
     *
     * @internal
     */
    public static function fromArray(array $arr): self
    {
        self::ensureAttachment($arr);
        self::ensureGherkinDocument($arr);
        self::ensureHook($arr);
        self::ensureMeta($arr);
        self::ensureParameterType($arr);
        self::ensureParseError($arr);
        self::ensurePickle($arr);
        self::ensureSource($arr);
        self::ensureStepDefinition($arr);
        self::ensureTestCase($arr);
        self::ensureTestCaseFinished($arr);
        self::ensureTestCaseStarted($arr);
        self::ensureTestRunFinished($arr);
        self::ensureTestRunStarted($arr);
        self::ensureTestStepFinished($arr);
        self::ensureTestStepStarted($arr);
        self::ensureUndefinedParameterType($arr);

        return new self(
            isset($arr['attachment']) ? Attachment::fromArray($arr['attachment']) : null,
            isset($arr['gherkinDocument']) ? GherkinDocument::fromArray($arr['gherkinDocument']) : null,
            isset($arr['hook']) ? Hook::fromArray($arr['hook']) : null,
            isset($arr['meta']) ? Meta::fromArray($arr['meta']) : null,
            isset($arr['parameterType']) ? ParameterType::fromArray($arr['parameterType']) : null,
            isset($arr['parseError']) ? ParseError::fromArray($arr['parseError']) : null,
            isset($arr['pickle']) ? Pickle::fromArray($arr['pickle']) : null,
            isset($arr['source']) ? Source::fromArray($arr['source']) : null,
            isset($arr['stepDefinition']) ? StepDefinition::fromArray($arr['stepDefinition']) : null,
            isset($arr['testCase']) ? TestCase::fromArray($arr['testCase']) : null,
            isset($arr['testCaseFinished']) ? TestCaseFinished::fromArray($arr['testCaseFinished']) : null,
            isset($arr['testCaseStarted']) ? TestCaseStarted::fromArray($arr['testCaseStarted']) : null,
            isset($arr['testRunFinished']) ? TestRunFinished::fromArray($arr['testRunFinished']) : null,
            isset($arr['testRunStarted']) ? TestRunStarted::fromArray($arr['testRunStarted']) : null,
            isset($arr['testStepFinished']) ? TestStepFinished::fromArray($arr['testStepFinished']) : null,
            isset($arr['testStepStarted']) ? TestStepStarted::fromArray($arr['testStepStarted']) : null,
            isset($arr['undefinedParameterType']) ? UndefinedParameterType::fromArray($arr['undefinedParameterType']) : null,
        );
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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