<?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 Step message in Cucumber's message protocol
 * @see https://github.com/cucumber/messages
 *
 * A step */
final class Step implements JsonSerializable
{
    use JsonEncodingTrait;

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

        /**
         * The location of the steps' `keyword`
         */
        public readonly Location $location = new Location(),

        /**
         * The actual keyword as it appeared in the source.
         */
        public readonly string $keyword = '',

        /**
         * The test phase signalled by the keyword: Context definition (Given), Action performance (When), Outcome assertion (Then). Other keywords signal Continuation (And and But) from a prior keyword. Please note that all translations which a dialect maps to multiple keywords (`*` is in this category for all dialects), map to 'Unknown'.
         */
        public readonly ?Step\KeywordType $keywordType = null,
        public readonly string $text = '',
        public readonly ?DocString $docString = null,
        public readonly ?DataTable $dataTable = null,

        /**
         * Unique ID to be able to reference the Step from PickleStep
         */
        public readonly string $id = '',
    ) {
    }

    /**
     * @throws SchemaViolationException
     *
     * @internal
     */
    public static function fromArray(array $arr): self
    {
        self::ensureLocation($arr);
        self::ensureKeyword($arr);
        self::ensureKeywordType($arr);
        self::ensureText($arr);
        self::ensureDocString($arr);
        self::ensureDataTable($arr);
        self::ensureId($arr);

        return new self(
            Location::fromArray($arr['location']),
            (string) $arr['keyword'],
            isset($arr['keywordType']) ? Step\KeywordType::from((string) $arr['keywordType']) : null,
            (string) $arr['text'],
            isset($arr['docString']) ? DocString::fromArray($arr['docString']) : null,
            isset($arr['dataTable']) ? DataTable::fromArray($arr['dataTable']) : null,
            (string) $arr['id'],
        );
    }

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

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

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

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

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

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

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