<?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 Meta message in Cucumber's message protocol
 * @see https://github.com/cucumber/messages
 *
 * This message contains meta information about the environment. Consumers can use
 * this for various purposes. */
final class Meta implements JsonSerializable
{
    use JsonEncodingTrait;

    /**
     * Construct the Meta with all properties
     *
     */
    public function __construct(

        /**
         * The [SEMVER](https://semver.org/) version number of the protocol
         */
        public readonly string $protocolVersion = '',

        /**
         * SpecFlow, Cucumber-JVM, Cucumber.js, Cucumber-Ruby, Behat etc.
         */
        public readonly Product $implementation = new Product(),

        /**
         * Java, Ruby, Node.js etc
         */
        public readonly Product $runtime = new Product(),

        /**
         * Windows, Linux, MacOS etc
         */
        public readonly Product $os = new Product(),

        /**
         * 386, arm, amd64 etc
         */
        public readonly Product $cpu = new Product(),
        public readonly ?Ci $ci = null,
    ) {
    }

    /**
     * @throws SchemaViolationException
     *
     * @internal
     */
    public static function fromArray(array $arr): self
    {
        self::ensureProtocolVersion($arr);
        self::ensureImplementation($arr);
        self::ensureRuntime($arr);
        self::ensureOs($arr);
        self::ensureCpu($arr);
        self::ensureCi($arr);

        return new self(
            (string) $arr['protocolVersion'],
            Product::fromArray($arr['implementation']),
            Product::fromArray($arr['runtime']),
            Product::fromArray($arr['os']),
            Product::fromArray($arr['cpu']),
            isset($arr['ci']) ? Ci::fromArray($arr['ci']) : null,
        );
    }

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

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

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

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

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

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