<?php

declare(strict_types=1);

namespace Webapp\Core\Command;

use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface;
use Webapp\Core\Entity\Algorithm;
use Webapp\Core\Entity\AlgorithmCondition;
use Webapp\Core\Entity\AlgorithmParameter;

/**
 * Class AddAlgorithmCommand insert a new algorithm entry
 */
class AddAlgorithmCommand extends Command
{
    const ARG_FILE = 'file';

    /**
     * @var EntityManagerInterface
     */
    private EntityManagerInterface $entityManager;

    private ContainerBagInterface $params;

    /**
     * CleanDeletedFileCommand constructor.
     * @param EntityManagerInterface $entityManager
     */
    public function __construct(
        EntityManagerInterface $entityManager,
        ContainerBagInterface  $params
    )
    {
        parent::__construct("adonis:algorithm:add");
        $this->entityManager = $entityManager;
        $this->params = $params;
    }

    /**
     * @see Command
     */
    protected function configure()
    {
        $this->setDescription(
            'Add a new protocol algorithm'
        );
        $this->addArgument(
            static::ARG_FILE,
            InputArgument::REQUIRED,
            'Name of the R algorithm file in the algorithm dir');
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return int
     * @see Command
     */
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $helper = $this->getHelper('question');
        $algo = new Algorithm();
        $algo->setScriptName($input->getArgument(static::ARG_FILE));

        chdir($this->params->get('algorithm.dir'));

        if (!is_file($algo->getScriptName())) {
            $output->writeln("Algorithm not found in " . $this->params->get('algorithm.dir'));
            return 1;
        }

        $question = new Question('Enter algorithm name : ');
        $algoName = $helper->ask($input, $output, $question);
        $algo->setName($algoName);

        $question = new ConfirmationQuestion('Does the algorithm need line repartition ? ');
        $repartition = $helper->ask($input, $output, $question);
        $algo->setWithRepartition($repartition);

        if($repartition){
            $question = new ConfirmationQuestion('Does the algorithm can generate subBlocks ? ');
            $subBlock = $helper->ask($input, $output, $question);
            $algo->setWithSubBlock($subBlock);
        }else {
            $algo->setWithSubBlock(false);
        }


        $output->writeln("You will be ask for the algorithms parameters, the order will be conserved for algorithm execution.");
        $output->writeln("Select \"Stop\" to stop adding new parameters");
        $parameterNameQuestion = new ChoiceQuestion('Choose one existing information that will be used as parameter or choose other to ask the user a param', [
            'Stop',
            AlgorithmParameter::SPECIAL_PARAM_CONSTANT_REPETITION,
            AlgorithmParameter::SPECIAL_PARAM_NBR_FACTOR,
            AlgorithmParameter::SPECIAL_PARAM_NBR_TREATMENT,
            'Other',
        ], 0);
        $question = new Question('Enter parameter name : ');
        while (true) {
            $paramName = $helper->ask($input, $output, $parameterNameQuestion);
            if (strtolower($paramName) === 'stop') {
                break;
            }
            $parameter = (new AlgorithmParameter());
            switch ($paramName) {
                case AlgorithmParameter::SPECIAL_PARAM_CONSTANT_REPETITION:
                case AlgorithmParameter::SPECIAL_PARAM_NBR_FACTOR:
                case AlgorithmParameter::SPECIAL_PARAM_NBR_TREATMENT:
                    $parameter->setSpecialParam($paramName);
                    break;
                default:
                    $paramName = $helper->ask($input, $output, $question);
            }
            $parameter->setName($paramName)
                ->setOrder($algo->getAlgorithmParameters()->count());
            $algo->addAlgorithmParameter($parameter);
        }

        $output->writeln("You will now be ask for the algorithms conditions");
        $output->writeln("Select \"Stop\" to stop adding new condition");
        $conditionNameQuestion = new ChoiceQuestion('Choose a condition', [
            'Stop',
            AlgorithmCondition::CONDITION_CONSTANT_REPETITION_NUMBER,
            AlgorithmCondition::CONDITION_MIN_2_FACTORS,
            AlgorithmCondition::CONDITION_REPETITION_EQUALS_TREATMENTS,
        ], 0);
        while (true) {
            $paramName = $helper->ask($input, $output, $conditionNameQuestion);
            if (strtolower($paramName) === 'stop') {
                break;
            }
            $condition = (new AlgorithmCondition());
            switch ($paramName) {
                case AlgorithmCondition::CONDITION_CONSTANT_REPETITION_NUMBER:
                case AlgorithmCondition::CONDITION_MIN_2_FACTORS:
                case AlgorithmCondition::CONDITION_REPETITION_EQUALS_TREATMENTS:
                $condition->setType($paramName);
                    break;
            }
            $algo->addAlgorithmCondition($condition);
        }

        $output->writeln("Algorithm name : $algoName");
        if ($algo->getAlgorithmParameters()->count() === 0) {
            $output->writeln("NO PARAMETERS");
        } else {
            foreach ($algo->getAlgorithmParameters() as $parameter) {
                $output->writeln("├── Parameter name : " . $parameter->getName());
            }
        }

        if ($algo->getAlgorithmConditions()->count() === 0) {
            $output->writeln("NO CONDITION");
        } else {
            $output->writeln("CONDITIONS");
            foreach ($algo->getAlgorithmConditions() as $condition) {
                $output->writeln("├── Condition type : " . $condition->getType());
            }
        }
        $question = new ConfirmationQuestion("Do you confirm the previous informations ?");
        if (!$helper->ask($input, $output, $question)) {
            return 0;
        }

        $output->writeln("The algorithm will now be tested");
        $params = [];
        $question = new Question('');
        $question->setValidator(function ($answer) {
            if (!is_numeric($answer)) {
                throw new \RuntimeException('The value must be a number');
            }
            return $answer;
        });
        foreach ($algo->getAlgorithmParameters() as $parameter){
            $output->writeln("Enter the value for ".$parameter->getName());
            $params[] = $helper->ask($input, $output, $question);
        }

        $outputFile = 'output.out';
        $script = "Rscript " . $algo->getScriptName() . " " . implode(" ", $params) . " " . $outputFile;
        $output->writeln($script);
        exec($script);

        $question = new ConfirmationQuestion("Confirm algorithm result ? (output.out) ");
        if (!$helper->ask($input, $output, $question)) {
            return 0;
        }

        $this->entityManager->persist($algo);
        $this->entityManager->flush();
        $output->writeln("Algorithm Created");

        return 0;
    }


}
