<?php

namespace WordpressModels\DependencyInjection\RestRouteAttributes\Attribute;

use Attribute;

/**
 * WordPress Rest Route attribute.
 *
 * Modelled after Symfony Router Routes.
 *
 * Can be targeted onto Classes and Methods.
 *
 * If this attribute is targeted to a method, without its parent class having this attribute,
 * all arguments work as expected.
 *
 * Otherwise:
 * * the `route` argument on the METHOD is prepended by the CLASS property (with a '/' in between).
 * * the `permission` argument on the METHOD overrides the class `permission` argument.
 * * the `methods` argument on the METHOD overrides the class `methods` argument.
 *      (leave empty on method to use class property)
 * * the `additionalProperties` argument on the METHOD is enhanced by the `additionalProperties` on the CLASS.
 *
 * For classes, `arguments`, `override`, and `public` properties are ignored.
 */
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
class RestRoute
{
    final public const METHODS = ['GET', 'POST', 'PATCH', 'PUT', 'DELETE'];

    final public const ARGUMENT_SHAPE = [
        'validate_callback' => 'callable',
        'sanitize_callback' => 'callable',
        'required' => 'bool',
        'default' => 'mixed',
        'type' => 'string'
    ];

    /**
     * @psalm-param array{self::METHODS} $methods
     * @psalm-param array{self::ARGUMENT_SHAPE} $arguments
     */
    public function __construct(
        public readonly string  $route,
        public readonly array   $methods = [],
        public readonly ?string $permission = null,
        public readonly array   $arguments = [],
        public readonly bool    $override = false,
        public readonly bool    $public = true,
        protected array         $additionalProperties = [],
        public readonly string  $namespace = ''
    )
    {
    }

    public function getAdditionalProperties(): array
    {
        return $this->additionalProperties;
    }

    public function addAdditionalProperty(string $key, mixed $value): void
    {
        $this->additionalProperties[$key] = $value;
    }

    public function removeAdditionalProperty(string $key): void
    {
        unset($this->additionalProperties[$key]);
    }
}
