<?php

declare(strict_types=1);

namespace App\Command;

use App\Controller\OgAgent\CreateImageAction;
use App\Controller\OgAgent\DeployImageAction;
use App\Controller\OgAgent\PartitionAssistantAction;
use App\Controller\OgAgent\RunScriptAction;
use App\Dto\Input\CommandExecuteInput;
use App\Dto\Input\DeployImageInput;
use App\Dto\Input\PartitionInput;
use App\Dto\Input\PartitionPostInput;
use App\Dto\Output\ClientOutput;
use App\Entity\CommandTask;
use App\Entity\Image;
use App\Entity\ImageImageRepository;
use App\Entity\Partition;
use App\Entity\Trace;
use App\Model\ClientStatus;
use App\Model\CommandTypes;
use App\Model\TraceStatus;
use App\Repository\CommandTaskRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;

#[AsCommand(name: 'opengnsys:run-scheduled-command-tasks', description: 'Run scheduled command tasks')]
class RunScheduledCommandTasksCommand extends Command
{
    public function __construct(
        private readonly CommandTaskRepository $commandTaskRepository,
        private readonly EntityManagerInterface $entityManager,
        private readonly RunScriptAction $runScriptAction,
        private readonly DeployImageAction $deployImageAction,
        private readonly CreateImageAction $createImageAction,
        private readonly PartitionAssistantAction $partitionAssistantAction,
    )
    {
        parent::__construct();
    }

    /**
     * @throws \Exception
     * @throws TransportExceptionInterface
     */
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $now = new \DateTimeImmutable('now');

        $tasks = $this->commandTaskRepository->findAll();

        foreach ($tasks as $task) {
            /** @var CommandTask $task */
            $nextExecution = $task->getNextExecution();

            if (!$nextExecution) {
                continue;
            }

            $difference = $now->getTimestamp() - $nextExecution->getTimestamp();

            $output->writeln("Now: " . $now->format('Y-m-d H:i:s T'));
            $output->writeln("NextExecution: " . $nextExecution->format('Y-m-d H:i:s T'));
            $output->writeln("Diferencia: $difference segundos para la tarea {$task->getName()}");

            if (abs($difference) < 30) {
                $output->writeln("Ejecutando tarea: " . $task->getName());

                $scripts = $task->getCommandTaskScripts()->toArray();
                usort($scripts, fn($a, $b) => $a->getExecutionOrder() <=> $b->getExecutionOrder());

                foreach ($scripts as $script) {
                    $output->writeln(" - Creando traza para script de tipo {$script->getType()} con orden {$script->getExecutionOrder()}");

                    $this->createTraceForScript($script, $task, $output);
                }

                $task->setLastExecution(new \DateTimeImmutable('now'));
                $task->setNextExecution($task->calculateNextExecutionDate());

                $this->entityManager->persist($task);
            }
        }

        $this->entityManager->flush();
        return Command::SUCCESS;
    }

    private function createTraceForScript($script, CommandTask $task, OutputInterface $output): void
    {
        $scriptParameters = $script->getParameters();
        
        if ($task->getScope() !== 'clients') {
            $clients = $task->getOrganizationalUnit()?->getClients();
        } else {
            $clients = $task->getClients();
        }

        foreach ($clients as $client) {
            $trace = new Trace();
            $trace->setClient($client);
            $trace->setStatus(TraceStatus::PENDING);
            $trace->setExecutedAt(new \DateTime());

            switch ($script->getType()) {
                case 'run-script':
                    $trace->setCommand(CommandTypes::RUN_SCRIPT);
                    $trace->setInput([
                        'script' => $script->getContent()
                    ]);
                    break;
                
                case 'deploy-image':
                    $trace->setCommand(CommandTypes::DEPLOY_IMAGE);
                    
                    $input = [
                        'imageImageRepository' => $scriptParameters['imageUuid']
                    ];
                    
                    if (isset($scriptParameters['method'])) {
                        $input['method'] = $scriptParameters['method'];
                    }
                    if (isset($scriptParameters['type'])) {
                        $input['type'] = $scriptParameters['type'];
                    }
                    if (isset($scriptParameters['diskNumber'])) {
                        $input['diskNumber'] = $scriptParameters['diskNumber'];
                    }
                    if (isset($scriptParameters['partitionNumber'])) {
                        $input['partitionNumber'] = $scriptParameters['partitionNumber'];
                    }
                    if (isset($scriptParameters['mcastMode'])) {
                        $input['mcastMode'] = $scriptParameters['mcastMode'];
                    }
                    if (isset($scriptParameters['mcastSpeed'])) {
                        $input['mcastSpeed'] = $scriptParameters['mcastSpeed'];
                    }
                    if (isset($scriptParameters['mcastPort'])) {
                        $input['mcastPort'] = $scriptParameters['mcastPort'];
                    }
                    if (isset($scriptParameters['mcastIp'])) {
                        $input['mcastIp'] = $scriptParameters['mcastIp'];
                    }
                    if (isset($scriptParameters['maxClients'])) {
                        $input['maxClients'] = $scriptParameters['maxClients'];
                    }
                    if (isset($scriptParameters['maxTime'])) {
                        $input['maxTime'] = $scriptParameters['maxTime'];
                    }
                    if (isset($scriptParameters['p2pMode'])) {
                        $input['p2pMode'] = $scriptParameters['p2pMode'];
                    }
                    if (isset($scriptParameters['p2pTime'])) {
                        $input['p2pTime'] = $scriptParameters['p2pTime'];
                    }
                    
                    $trace->setInput($input);
                    break;
                
                case 'create-image':
                    $trace->setCommand(CommandTypes::CREATE_IMAGE);
                    $trace->setInput([
                        'type' => $scriptParameters['type'],
                        'imageName' => $scriptParameters['imageName'],
                        'imageUuid' => $scriptParameters['imageUuid'] ?? null,
                        'diskNumber' => $scriptParameters['diskNumber'] ?? null,
                        'partitionNumber' => $scriptParameters['partitionNumber'] ?? null,
                        'gitRepositoryName' => $scriptParameters['gitRepositoryName'] ?? null,
                        'action' => $scriptParameters['action'] ?? 'create'
                    ]);
                    break;
                
                case 'partition-assistant':
                    $trace->setCommand(CommandTypes::PARTITION_AND_FORMAT);
                    $trace->setInput($scriptParameters);
                    break;
                
                case 'power-on':
                    $trace->setCommand(CommandTypes::POWER_ON);
                    $trace->setInput($scriptParameters ?? []);
                    break;
                
                case 'reboot':
                    $trace->setCommand(CommandTypes::REBOOT);
                    $trace->setInput($scriptParameters ?? []);
                    break;
                
                case 'shutdown':
                    $trace->setCommand(CommandTypes::SHUTDOWN);
                    $trace->setInput($scriptParameters ?? []);
                    break;
                
                case 'login':
                    $trace->setCommand(CommandTypes::LOGIN);
                    $trace->setInput($scriptParameters ?? []);
                    break;
 
                default:
                    $output->writeln(" - Tipo de script no soportado: {$script->getType()}");
                    continue 2; 
            }

            $this->entityManager->persist($trace);
            $output->writeln(" - Traza creada para cliente {$client->getUuid()} con comando {$trace->getCommand()}");
        }
    }


    private function executePartitionAssistant($script, CommandTask $task, OutputInterface $output): void
    {
        $scriptParameters = $script->getParameters();
        
        $output->writeln(" - Debug: Parameters = " . ($scriptParameters ? json_encode($scriptParameters) : 'null'));
        
        if (!$scriptParameters) {
            $output->writeln(" - Error: Parámetros del script vacíos o nulos");
            return;
        }

        if (!is_array($scriptParameters)) {
            $output->writeln(" - Error: Los parámetros deben ser un array");
            return;
        }

        foreach ($task->getOrganizationalUnit()?->getClients() as $client) {
            $partitionInput = new PartitionPostInput();
            $partitionInput->clients = [new ClientOutput($client)];
            
            $partitions = [];
            
            foreach ($scriptParameters as $partitionData) {
                if (isset($partitionData['removed']) && $partitionData['removed']) {
                    continue;
                }
                
                if (!isset($partitionData['size']) || $partitionData['size'] <= 0) {
                    continue;
                }
                
                $partitionInputObj = new PartitionInput();
                $partitionInputObj->diskNumber = $partitionData['diskNumber'] ?? 1;
                $partitionInputObj->partitionNumber = $partitionData['partitionNumber'] ?? 1;
                $partitionInputObj->partitionCode = $partitionData['partitionCode'] ?? 'LINUX';
                $partitionInputObj->size = (float)($partitionData['size'] / 1024);
                $partitionInputObj->filesystem = $partitionData['filesystem'] ?? 'EXT4';
                $partitionInputObj->format = $partitionData['format'] ?? false;
                $partitionInputObj->memoryUsage = $partitionData['memoryUsage'] ?? 0;
                
                $partitions[] = $partitionInputObj;
            }
            
            if (empty($partitions)) {
                $output->writeln(" - Warning: No hay particiones válidas para procesar");
                continue;
            }
            
            $partitionInput->partitions = $partitions;
            $partitionInput->queue = false;

            try {
                $response = $this->partitionAssistantAction->__invoke($partitionInput, null);
                
                $output->writeln(" - Partition assistant iniciado para cliente {$client->getUuid()}");
            } catch (\Exception $e) {
                $output->writeln(" - Error en partition assistant para cliente {$client->getUuid()}: " . $e->getMessage());
            }
        }
    }
}
