<?php

namespace App\Controller\OgAgent;

use App\Entity\Client;
use App\Entity\Hardware;
use App\Entity\HardwareProfile;
use App\Entity\HardwareType;
use App\Model\CommandTypes;
use App\Model\TraceStatus;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Validator\Exception\ValidatorException;
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 HardwareInventoryAction extends AbstractOgAgentController
{
    /**
     * @throws TransportExceptionInterface
     * @throws ServerExceptionInterface
     * @throws RedirectionExceptionInterface
     * @throws ClientExceptionInterface
     */
    public function __invoke(Client $client): JsonResponse
    {
        $data = [
            'nfn' => 'InventarioHardware',
            'ids' => '0'
        ];

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

        if (isset($response['error']) && $response['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) {
            throw new BadRequestHttpException('Error performing hardware inventory: '.$response['error']);
        }

        $responseData = $response;
        
        if (isset($responseData['res']) && $responseData['res'] === 2) {
            throw new BadRequestHttpException('Error en el inventario de hardware: operación fallida');
        }

        $this->logger->info('Hardware inventory for client', ['client' => $client->getId()]);

        $inputData = [
            'client' => $client->getIp(),
        ];

        $this->createService->__invoke($client, CommandTypes::HARDWARE_INVENTORY, TraceStatus::SUCCESS, null, $inputData);

        if (isset($responseData['res']) && $responseData['res'] === 1) {
            $this->processHardwareInventory($client, $responseData);
            
            return new JsonResponse(data: $responseData, status: Response::HTTP_OK);
        }

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

    private function processHardwareInventory(Client $client, array $responseData): void
    {
        if (!isset($responseData['hrd'])) {
            $this->logger->warning('No hay datos de hardware en la respuesta', ['client' => $client->getId()]);
            return;
        }

        $this->logger->info('Iniciando procesamiento de inventario de hardware', ['client' => $client->getId()]);

        $hardwareData = base64_decode($responseData['hrd']);
        
        if ($hardwareData === false) {
            $this->logger->error('Error decodificando datos de hardware base64', ['client' => $client->getId()]);
            return;
        }

        $this->logger->debug('Datos de hardware decodificados', [
            'client' => $client->getId(),
            'data_length' => strlen($hardwareData)
        ]);

        $hardwareLines = explode("\n", $hardwareData);
        $this->logger->info('Líneas de hardware encontradas', [
            'client' => $client->getId(),
            'total_lines' => count($hardwareLines)
        ]);
        
        $hardwareRepository = $this->entityManager->getRepository(Hardware::class);
        $hardwareTypeRepository = $this->entityManager->getRepository(HardwareType::class);

        $hardwareItems = [];
        $processedLines = 0;
        $createdHardware = 0;

        foreach ($hardwareLines as $line) {
            $line = trim($line);
            if (empty($line)) {
                continue;
            }

            $parts = explode('=', $line, 2);
            if (count($parts) !== 2) {
                $this->logger->debug('Línea de hardware ignorada (formato incorrecto)', [
                    'client' => $client->getId(),
                    'line' => $line
                ]);
                continue;
            }

            $typeCode = trim($parts[0]);
            $description = trim($parts[1]);

            if (empty($typeCode) || empty($description)) {
                $this->logger->debug('Línea de hardware ignorada (datos vacíos)', [
                    'client' => $client->getId(),
                    'line' => $line
                ]);
                continue;
            }

            $hardwareType = $this->getOrCreateHardwareType($typeCode, $hardwareTypeRepository);
            
            if ($hardwareType->getId() === null) {
                $this->logger->debug('Nuevo tipo de hardware creado', [
                    'client' => $client->getId(),
                    'type_name' => $hardwareType->getName(),
                    'type_code' => $typeCode
                ]);
            }

            $hardware = $hardwareRepository->findOneBy([
                'name' => $description,
                'type' => $hardwareType
            ]);

            if (!$hardware) {
                $hardware = new Hardware();
                $hardware->setName($description);
                $hardware->setDescription($description);
                $hardware->setType($hardwareType);
                $this->entityManager->persist($hardware);
                $createdHardware++;
                $this->logger->debug('Nuevo hardware creado', [
                    'client' => $client->getId(),
                    'hardware_name' => $description,
                    'hardware_type' => $hardwareType->getName()
                ]);
            }

            $hardwareItems[] = $hardware;
            $processedLines++;
        }

        $this->logger->info('Procesamiento de líneas de hardware completado', [
            'client' => $client->getId()
        ]);

        $hardwareProfile = $client->getHardwareProfile();
        $isNewProfile = false;
        
        if (!$hardwareProfile) {
            $hardwareProfile = new HardwareProfile();
            $hardwareProfile->setDescription('Perfil de hardware generado automáticamente para ' . $client->getIp());
            $hardwareProfile->setClient($client);
            $this->entityManager->persist($hardwareProfile);
            $isNewProfile = true;
            
            $this->logger->info('Nuevo perfil de hardware creado', [
                'client' => $client->getId(),
                'client_ip' => $client->getIp()
            ]);
            
            $client->setHardwareProfile($hardwareProfile);
        } else {
            $this->logger->info('Actualizando perfil de hardware existente', [
                'client' => $client->getId(),
                'profile_id' => $hardwareProfile->getId()
            ]);
        }

        foreach ($hardwareItems as $hardware) {
            $hardwareProfile->addHardwareCollection($hardware);
        }

        $this->logger->debug('Nuevo hardware añadido al perfil', [
            'client' => $client->getId(),
            'added_hardware_count' => count($hardwareItems)
        ]);

        $client->setHardwareProfile($hardwareProfile);
        $this->entityManager->persist($client);

        $this->entityManager->flush();

        $this->logger->info('Inventario de hardware guardado exitosamente', [
            'client' => $client->getId(),
            'profile_id' => $hardwareProfile->getId(),
            'is_new_profile' => $isNewProfile,
            'total_hardware_items' => count($hardwareItems)
        ]);
    }

    private function getOrCreateHardwareType(string $typeCode, $hardwareTypeRepository): HardwareType
    {
        $hardwareTypeMapping = [
            'boo' => 'BIOS',
            'mod' => 'Motherboard',
            'boa' => 'Board',
            'cpu' => 'CPU',
            'mem' => 'Memory',
            'ide' => 'IDE Controller',
            'vga' => 'Graphics Card',
            'dis' => 'Disk',
            'usb' => 'USB Controller',
            'cdr' => 'CD/DVD',
            'net' => 'Network Card'
        ];

        $hardwareType = $hardwareTypeRepository->findOneBy(['code' => $typeCode]);
        
        if (!$hardwareType) {
            $typeName = $hardwareTypeMapping[$typeCode] ?? strtoupper($typeCode);
            $hardwareType = $hardwareTypeRepository->findOneBy(['name' => $typeName]);
        }
        
        if (!$hardwareType) {
            $typeName = $hardwareTypeMapping[$typeCode] ?? strtoupper($typeCode);
            $hardwareType = new HardwareType();
            $hardwareType->setName($typeName);
            $hardwareType->setCode($typeCode);
            
            if (isset($hardwareTypeMapping[$typeCode])) {
                $descriptions = [
                    'boo' => 'Sistema básico de entrada/salida',
                    'mod' => 'Placa base del sistema',
                    'boa' => 'Placa base o tarjeta madre',
                    'cpu' => 'Procesador central',
                    'mem' => 'Memoria RAM',
                    'ide' => 'Controlador IDE',
                    'vga' => 'Tarjeta gráfica',
                    'dis' => 'Disco de almacenamiento',
                    'usb' => 'Controlador USB',
                    'cdr' => 'Unidad de CD/DVD',
                    'net' => 'Tarjeta de red'
                ];
                
                $hardwareType->setDescription($descriptions[$typeCode] ?? null);
            }
            
            $this->entityManager->persist($hardwareType);
        }
        
        return $hardwareType;
    }
}