<?php

declare(strict_types=1);

namespace App\Controller\OgAgent;

use App\Dto\Input\PartitionPostInput;
use App\Entity\Client;
use App\Entity\Partition;
use App\Entity\Trace;
use App\Model\ClientStatus;
use App\Model\CommandTypes;
use App\Model\TraceStatus;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;

class PartitionAssistantAction extends AbstractOgAgentController
{
    /**
     * @throws TransportExceptionInterface
     * @throws ServerExceptionInterface
     * @throws RedirectionExceptionInterface
     * @throws ClientExceptionInterface
     */
    public function __invoke(PartitionPostInput $input, ?Trace $existingTrace = null): JsonResponse
    {
        $partitions = $input->partitions;

        if (empty($partitions)) {
            throw new BadRequestHttpException('Partitions is required');
        }

        $clientJobs = [];

        foreach ($input->clients as $clientInput) {
            /** @var Client $client */
            $client = $clientInput->getEntity();

            $disks = [];
            foreach ($partitions as $partition) {
                $diskNumber = $partition->diskNumber;

                $partitionEntity = $this->entityManager->getRepository(Partition::class)->findOneBy([
                    'client' => $client,
                    'partitionNumber' => $partition->partitionNumber,
                    'diskNumber' => $partition->diskNumber,
                ]);

                if ($partitionEntity) {
                    $partitionEntity->setClient($client);
                    $this->entityManager->persist($partitionEntity);
                }

                if (!isset($disks[$diskNumber])) {
                    $disks[$diskNumber] = [
                        'diskData' => [],
                        'partitionData' => []
                    ];
                }

                $disks[$diskNumber]['diskData'] = [
                    'dis' => (string) $diskNumber,
                    'tch' => (string) ($partition->size * 1024),
                ];

                $disks[$diskNumber]['partitionData'][] = [
                    'par' => (string) $partition->partitionNumber,
                    'cpt' => $partition->partitionCode,
                    'sfi' => $partition->filesystem,
                    'tam' => (string) (integer) ($partition->size * 1024),
                    'ope' => $partition->format ? "1" : "0",
                ];
            }

            foreach ($disks as $diskNumber => $diskInfo) {
                $data = [];
                if (!empty($diskInfo['diskData'])) {
                    $data[] = $diskInfo['diskData'];
                }
                $data = array_merge($data, $diskInfo['partitionData']);

                $result = [
                    "nfn" => "Configurar",
                    "dsk" => (string) $diskNumber,
                    "cfg" => $data,
                    "ids" => "0"
                ];

                $response = $this->createRequest(
                    method: 'POST',
                    url: 'https://'.$client->getIp().':8000/opengnsys/Configurar',
                    params: [
                        'json' => $result,
                    ],
                    token: $client->getToken(),
                );

                $this->logger->info('Partition assistant', ['client' => $client->getId()]);

                if (isset($response['error']) && $response['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) {
                    if ($input->queue) {
                        $this->createService->__invoke($client, CommandTypes::PARTITION_AND_FORMAT, TraceStatus::PENDING, null, $data);
                        continue;
                    }

                    continue;
                }

                $jobId = $response['job_id'];

                $client->setStatus(ClientStatus::BUSY);
                $this->entityManager->persist($client);
                $this->entityManager->flush();

                if ($existingTrace) {
                    $existingTrace->setStatus(TraceStatus::IN_PROGRESS);
                    $existingTrace->setJobId($jobId);
                    $this->entityManager->persist($existingTrace);
                    $this->entityManager->flush();
                } else {
                    $this->createService->__invoke($client, CommandTypes::PARTITION_AND_FORMAT, TraceStatus::IN_PROGRESS, $jobId, $data);
                }

                $clientJobs['/clients/' . $client->getUuid()] = $jobId;
            }
        }

        return new JsonResponse(data: $clientJobs, status: Response::HTTP_OK);
    }
}
