<?php declare(strict_types = 1);

namespace idoit\Module\SyneticsFlows\Automation\Action\ActionType\IdoitCommand;

use idoit\Module\SyneticsFlows\Automation\Action\ActionType\CallIdoitCommandAction;
use idoit\Module\SyneticsFlows\Automation\Action\ActionType\IdoitCommand\Argument\IdoitCliArgument;
use idoit\Module\SyneticsFlows\Automation\Action\ActionType\IdoitCommand\Option\IdoitCliOption;
use idoit\Module\SyneticsFlows\Serialization\ArrayFormat;
use idoit\Module\SyneticsFlows\Serialization\SerializableTrait;
use idoit\Module\SyneticsFlows\Validation\ValidationMessage;
use JsonSerializable;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;

class IdoitCommand implements JsonSerializable
{
    use SerializableTrait;

    private string $description;

    /**
     * @param string $name
     * @param string $description
     * @param IdoitCliOption[] $options
     * @param IdoitCliArgument[] $arguments
     */
    public function __construct(
        private string $name,
        string $description,
        #[ArrayFormat(IdoitCliOption::class)]
        private array $options,
        #[ArrayFormat(IdoitCliArgument::class)]
        private array $arguments,
    )
    {
        $this->description = strip_tags($description);
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function getDescription(): string
    {
        return $this->description;
    }

    /**
     * @return IdoitCliOption[]
     */
    public function getOptions(): array
    {
        return $this->options;
    }

    public function getArguments(): array
    {
        return $this->arguments;
    }

    public function validate(CallIdoitCommandAction $action): array
    {
        $parameters = $action->getParameters() ?? [];
        $errors = [];

        foreach ($this->options as $option) {
            $value = $parameters[$option->getName()] ?? null;

            foreach ($option->validate($value, $parameters) as $error) {
                if (!$error instanceof ValidationMessage) {
                    continue;
                }
                $errors[] = $error->prepend(['parameters']);
            }
        }

        foreach ($this->arguments as $argument) {
            $value = $parameters[$argument->getName()] ?? null;

            foreach ($argument->validate($value) as $error) {
                if (!$error instanceof ValidationMessage) {
                    continue;
                }
                $errors[] = $error->prepend(['parameters']);
            }
        }

        return $errors;
    }

    /**
     * @param InputInterface $input
     * @return array
     */
    public function prepareParameters(InputInterface $input): array
    {
        $parameters = [];

        foreach ($input->getOptions() as $optionName => $value) {
            if ($value !== null) {
                $parameters["--$optionName"] = $value;
            }
        }

        foreach ($input->getArguments() as $argName => $value) {
            if ($value !== null) {
                $parameters[$argName] = $value;
            }
        }

        return $parameters;
    }

    /**
     * @param array $parameters
     * @param InputDefinition $definition
     * @return InputInterface
     */
    public function prepareInput(array $parameters, InputDefinition $definition): InputInterface
    {
        $input = new ArrayInput([], $definition);

        foreach ($this->options as $option) {
            $value = $parameters[$option->getName()] ?? null;
            $option->apply($input, $value);
        }

        foreach ($this->arguments as $argument) {
            $value = $parameters[$argument->getName()] ?? null;
            $argument->apply($input, $value);
        }

        return new ArrayInput($this->prepareParameters($input), $definition);
    }
}