<?php

// --- SERVIR IMÁGENES DESDE LA URL ---
if (isset($_GET['img'])) {
    $imageName = basename($_GET['img']);
    $imagePath = __DIR__ . '/img/' . $imageName;
    if (file_exists($imagePath)) {
        $mimeType = mime_content_type($imagePath);
        header('Content-Type: ' . $mimeType);
        readfile($imagePath);
        exit;
    } else {
        http_response_code(404);
        echo 'Imagen no encontrada';
        exit;
    }
}

// --- SERVIR CONTRASEÑA GENERADA POR AJAX ---
if (isset($_GET['generate_password'])) {
    echo generate_strong_password();
    exit;
}

// --- ENDPOINT PARA ACTUALIZAR CONTRASEÑA DEL CLIENTE ---
if (isset($_POST['update_password']) && isset($_POST['clientId']) && isset($_POST['password'])) {
    // log
    $logger = \Ubnt\UcrmPluginSdk\Service\PluginLogManager::create();
    $logger->appendLog('POST update_password recibido: ' . var_export($_POST, true));
    // lógica PATCH
    $result = updateClientPasswordAttribute($_POST['clientId'], $_POST['password']);
    $logger->appendLog('Resultado updateClientPasswordAttribute: ' . var_export($result, true));
    header('Content-Type: application/json');
    echo json_encode($result);
    exit;
}

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
chdir(__DIR__);

require_once __DIR__ . '/vendor/autoload.php';

// --- ENDPOINT PARA ACTUALIZAR CONTRASEÑA DEL CLIENTE ---
if (isset($_POST['update_password']) && isset($_POST['clientId']) && isset($_POST['password'])) {
    $logger = \Ubnt\UcrmPluginSdk\Service\PluginLogManager::create();
    $logger->appendLog('POST update_password recibido: ' . var_export($_POST, true));
    $clientId = $_POST['clientId'];
    $password = $_POST['password'];
    $result = updateClientPasswordAttribute($clientId, $password);
    $logger->appendLog('Resultado updateClientPasswordAttribute: ' . var_export($result, true));
    header('Content-Type: application/json');
    echo json_encode($result);
    exit;
}

// Get UCRM log manager.
$log = \Ubnt\UcrmPluginSdk\Service\PluginLogManager::create();
// $log->appendLog(
//     sprintf(
//         'Executed from public URL: %s',
//         file_get_contents('php://input')
//     )
// );


// Variable para almacenar la respuesta de la API, mensajes de error y estado

$respuestaAPI = '';
$esError = false;

$clientFullName = '';
$IpAddressClientId = '';
$clientIdentifierType = 'ID'; // Puede ser 'ID', 'IP', 'MAC'

//variables para mostrar info del cliente
$clientFullName = '';
$IpAddressClientId = '';

// Ensure global variables are initialized at the top of the script
$clientFullName = $clientFullName ?? '';
$IpAddressClientId = $IpAddressClientId ?? '';

// Verificar si se ha enviado el formulario
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
/**
 * Actualiza el atributo personalizado 'passwordAntenaCliente' de un cliente en UCRM.
 *
 * @param int|string $clientId ID del cliente
 * @param string $newPassword Nueva contraseña a setear
 * @return array [success => bool, message => string]
 */
function updateClientPasswordAttribute($clientId, $newPassword) {
    $configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create();
    $config = $configManager->loadConfig();
    $baseUri = $config['ipserver'];
    $crmAuthToken = $config['apitoken']; // O $config['ucrmApiToken'] si así lo tienes
    $clientUcrm = new \GuzzleHttp\Client([
        'base_uri' => 'https://' . $baseUri . '/crm/api/v1.0/',
        'verify' => false
    ]);
    $logger = \Ubnt\UcrmPluginSdk\Service\PluginLogManager::create();

    try {
        // 1. Obtener el cliente actual
        $response = $clientUcrm->get('clients/' . $clientId, [
            'headers' => [
                'X-Auth-Token' => $crmAuthToken,
                'Content-Type' => 'application/json'
            ]
        ]);
        if ($response->getStatusCode() !== 200) {
            return ['success' => false, 'message' => 'No se pudo obtener el cliente.'];
        }
        $clientData = json_decode($response->getBody(), true);

        // 2. Buscar el atributo personalizado 'passwordAntenaCliente'
        $found = false;
        $attributes = [];
        if (isset($clientData['attributes']) && is_array($clientData['attributes'])) {
            foreach ($clientData['attributes'] as $attr) {
                if ($attr['key'] === 'passwordAntenaCliente') {
                    $attr['value'] = $newPassword;
                    $found = true;
                }
                $attributes[] = $attr;
            }
        }
        if (!$found) {
            return ['success' => false, 'message' => 'No se encontró el atributo personalizado passwordAntenaCliente.'];
        }

        // 3. Actualizar el cliente completo usando PATCH
        // Solo enviar el array attributes con customAttributeId y value
        $logger->appendLog('PATCH endpoint: clients/' . $clientId);
        $patchBody = [
            'attributes' => [
                [
                    'customAttributeId' => 17,
                    'value' => $newPassword
                ]
            ]
        ];
        $logger->appendLog('PATCH body: ' . json_encode($patchBody));
        try {
            $patchResponse = $clientUcrm->patch('clients/' . $clientId, [
                'headers' => [
                    'X-Auth-Token' => $crmAuthToken,
                    'Content-Type' => 'application/json'
                ],
                'json' => $patchBody
            ]);
            $logger->appendLog('PATCH status: ' . $patchResponse->getStatusCode());
            $logger->appendLog('PATCH response: ' . $patchResponse->getBody());
            if ($patchResponse->getStatusCode() === 200) {
                return ['success' => true, 'message' => 'Contraseña actualizada correctamente.'];
            } else {
                return ['success' => false, 'message' => 'Error al actualizar el cliente.'];
            }
        } catch (Exception $e) {
            $logger->appendLog('PATCH exception: ' . $e->getMessage());
            return ['success' => false, 'message' => 'Excepción: ' . $e->getMessage()];
        }
    } catch (Exception $e) {
        $logger->appendLog('PATCH attributes exception: ' . $e->getMessage());
        return ['success' => false, 'message' => 'Excepción: ' . $e->getMessage()];
    }
}
    $entrada = trim($_POST['identificador']);

    // Validar si es una dirección IP válida
    if (filter_var($entrada, FILTER_VALIDATE_IP)) {
        // Es una dirección IP válida
        $respuestaAPI = getVaultCredentials($entrada);
    }
    // Validar si es una dirección MAC válida
    elseif (preg_match('/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/', $entrada)) {
        // Es una dirección MAC válida
        $respuestaAPI = getVaultCredentials($entrada);
    }
    // Validar si es un ID numérico menor a 10,000
    elseif (ctype_digit($entrada) && intval($entrada) < 10000) {
        // Es un ID válido
        $respuestaAPI = getVaultCredentials($entrada);
    } else {
        // Entrada no válida
        $mensajeError = "La entrada no es válida. Por favor, ingresa una dirección IP, una dirección MAC o un ID menor a 10,000.";
    }

    // Verificar si la respuesta de la API indica un error
    if (strpos($respuestaAPI, "Error:") === 0) {
        $esError = true;
    }
}

function setNewPassword($clientId, $newPassword)
{
    // Aquí iría la lógica para actualizar la contraseña del cliente en UCRM
    // Usando la API de UCRM y el cliente Guzzle
    // Retornar true si se actualizó correctamente, false en caso contrario
    global $clientFullName, $IpAddressClientId;
    $logger = \Ubnt\UcrmPluginSdk\Service\PluginLogManager::create();
    $configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create();
    $config = $configManager->loadConfig();
    $baseUri = $config['ipserver'];
    $crmAuthToken = $config['apitoken'];
    $unmsAuthToken = $config['unmsApiToken'];
    $ucrmBaseUri = 'https://' . $baseUri . '/crm/api/v1.0/';

     $clientUcrm = new Client([
        'base_uri' => $ucrmBaseUri,
        'verify' => false // Deshabilitar la verificación del certificado SSL
    ]);

    try {
        $responseClient = $clientUcrm->patch('clients/' . $clientId, [
            'headers' => [
                'X-Auth-Token' => $crmAuthToken,
                'Content-Type' => 'application/json'
            ],
            'json' => [
                'attributes' => [
                    [
                        'customAttributeId' => 17, // ID del atributo personalizado 'passwordAntenaCliente'
                        'value' => $newPassword
                    ]
                ]
            ]
        ]);

        if ($responseClient->getStatusCode() === 200) {
            $logger->appendLog('Contraseña actualizada correctamente para el cliente: ' . $clientFullName . ' (ID: ' . $IpAddressClientId . ')');
            return true;
        } else {
            $logger->appendLog('Error al actualizar la contraseña. Código de estado HTTP: ' . $responseClient->getStatusCode());
            return false;
        }
    } catch (RequestException $e) {
        $logger->appendLog('Excepción al actualizar la contraseña: ' . $e->getMessage());
        return false;
    }


}
function getVaultCredentials($dataToSearch): string
{
    global $clientFullName, $IpAddressClientId, $clientIdentifierType;
    $logger = \Ubnt\UcrmPluginSdk\Service\PluginLogManager::create();
    $configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create();
    $config = $configManager->loadConfig();
    $baseUri = $config['ipserver'];
    $crmAuthToken = $config['apitoken'];
    $unmsAuthToken = $config['unmsApiToken'];


    $unmsBaseUri = 'https://' . $baseUri . '/nms/api/v2.1/';
    $ucrmBaseUri = 'https://' . $baseUri . '/crm/api/v1.0/';
    //$authToken = '7adc9198-50b1-41d0-9bfa-d4946902ed89';

    // Crear una instancia del cliente Guzzle
    $clientUnms = new Client([
        'base_uri' => $unmsBaseUri,
        'verify' => false // Deshabilitar la verificación del certificado SSL
    ]);

    $clientUcrm = new Client([
        'base_uri' => $ucrmBaseUri,
        'verify' => false // Deshabilitar la verificación del certificado SSL
    ]);


    //validar si dataToSearch es una dirección IP o una dirección MAC
    if (filter_var($dataToSearch, FILTER_VALIDATE_IP)) {
        $clientIdentifierType = 'IP';
        // La variable es una dirección IP válida
        $logger->appendLog('Consulta por dirección IP: ' . $dataToSearch);

        $IpAddressClientId = filter_var($dataToSearch, FILTER_VALIDATE_IP);

        try {
            $responseSitesByIP = $clientUnms->request('GET', 'sites/search?query=' . $dataToSearch . '&count=1&page=1', [
                'headers' => [
                    'X-Auth-Token' => $unmsAuthToken
                ]
            ]); //Sites por IP

            if ($responseSitesByIP->getStatusCode() === 200) {

                $datasSitesByIP = json_decode($responseSitesByIP->getBody(), true);
                $jsonDevicesBySite = json_encode($datasSitesByIP, JSON_PRETTY_PRINT);
                //print_r($jsonDevicesBySite . PHP_EOL); //Devices por ID del sitio

                if (isset($datasSitesByIP[0])) {
                    $siteId = $datasSitesByIP[0]['id'];
                    // print_r('ID DEL SITIO: ' . $siteId . PHP_EOL); // ID del sitio
                } else {
                    $logger->appendLog('No se encontró ningún sitio para la IP proporcionada: ' . $dataToSearch);
                    return 'Error: No se encontró ningún sitio para la IP proporcionada: ' . $dataToSearch; // Return early if no site is found
                }


            } else {
                //echo "Error en la solicitud. Código de estado HTTP: " . $responseSitesByIP->getStatusCode() . PHP_EOL;
                $logger->appendLog('Error en la solicitud. Código de estado HTTP: ' . $responseSitesByIP->getStatusCode());
                return 'Error: Falla en la solicitud. Código de estado HTTP: ' . $responseSitesByIP->getStatusCode(); // Return early if the request fails
            }

            $devicesBySiteId = $clientUnms->request('GET', 'devices?siteId=' . $siteId, [
                'headers' => [
                    'X-Auth-Token' => $unmsAuthToken
                ]
            ]); //Devices por ID del sitio

            if ($devicesBySiteId->getStatusCode() === 200) {
                $dataDevicesBySite = json_decode($devicesBySiteId->getBody(), true);
                $jsonDevicesBySite = json_encode($dataDevicesBySite, JSON_PRETTY_PRINT);
                //print_r($jsonDevicesBySite . PHP_EOL); //Devices por ID del sitio
                $deviceID = null;

                foreach ($dataDevicesBySite as $device) {
                    if (isset($device['ipAddress'])) {
                        $ipAddress = explode('/', $device['ipAddress'])[0]; // Obtener solo la IP sin la máscara
                        if ($ipAddress === $IpAddressClientId) {
                            $deviceID = $device['identification']['id']; // ID del dispositivo
                            break; // Salir del ciclo si se encuentra la IP
                        }
                    } else {
                        $logger->appendLog('No se encontró la IP del dispositivo en la respuesta de la API.');
                        return 'Error: No se encontró la IP del dispositivo en la respuesta de la API.'; // Return early if the IP is not found
                    }
                }

                if ($deviceID == null) {
                    //echo "No se encontró el dispositivo con la IP proporcionada." . PHP_EOL;
                    $logger->appendLog('No se encontró el dispositivo con la IP proporcionada: ' . $IpAddressClientId);
                    return 'Error: No se encontró el dispositivo con la IP proporcionada: ' . $IpAddressClientId; // Return early if no device is found 
                }

            } else {
                // echo "Error en la solicitud. Código de estado HTTP: " . $devicesBySiteId->getStatusCode() . PHP_EOL;
                $logger->appendLog('Error en la solicitud. Código de estado HTTP: ' . $devicesBySiteId->getStatusCode());
                return 'Error: Falla en la solicitud. Código de estado HTTP: ' . $devicesBySiteId->getStatusCode(); // Return early if the request fails
            }

            $responsePasswordVault = $clientUnms->request('GET', 'vault/' . $deviceID . '/credentials', [
                'headers' => [
                    'X-Auth-Token' => $unmsAuthToken
                ]
            ]);

            if ($responsePasswordVault->getStatusCode() === 200) {
                $dataPasswordVault = json_decode($responsePasswordVault->getBody(), true);
                // $jsonPasswordVault = json_encode($dataPasswordVault, JSON_PRETTY_PRINT);
                //print_r($jsonPasswordVault . PHP_EOL); //Cred

                if (isset($dataPasswordVault['credentials'][0]['password'])) {
                    //asignar el campo password a la variable dataPasswordVault
                    $dataPasswordVault = $dataPasswordVault['credentials'][0]['password'];
                } else {
                    // echo "No se encontró la contraseña en la respuesta de la API." . PHP_EOL;
                    $logger->appendLog('No se encontró una contraseña en la bóveda para la antena de este cliente, es altamente probable que conserve una contraseña conocida.');
                    return "Error: No se encontró una contraseña en la bóveda para la antena de este cliente, es altamente probable que conserve una contraseña conocida."; // Return early if the password is not found
                }
                return $dataPasswordVault;

            } else {
                // echo "Error en la solicitud. Código de estado HTTP: " . $responsePasswordVault->getStatusCode() . PHP_EOL;
                $logger->appendLog('Error en la solicitud. Código de estado HTTP: ' . $responsePasswordVault->getStatusCode());
                return 'Error: Falla en la solicitud. Código de estado HTTP: ' . $responsePasswordVault->getStatusCode(); // Return early if the request fails
            }


        } catch (RequestException $requestException) {
            // Manejar errores de la solicitud, si hay error 404 entonces responder que no se encontró niguna IP asociada a alguna antena o dispositivo de red
            if ($requestException->hasResponse()) {
                $response = $requestException->getResponse();
                $statusCode = $response->getStatusCode();
                $reason = $response->getReasonPhrase();
                // echo "Error: {$statusCode} - {$reason}" . PHP_EOL;
                // echo $response->getBody();
                if ($statusCode == 404) {
                    // echo "No se encontró el cliente con la dirección IP proporcionada." . PHP_EOL;
                    $logger->appendLog('No se encontró el cliente con la dirección IP proporcionada: ' . $IpAddressClientId);
                    return 'Error: No se encontró el cliente con la dirección IP proporcionada: ' . $IpAddressClientId; // Return early if the client is not found
                }
                return 'Error: ' . $reason; // Return early if the request fails
            } else {
                // echo "Error: " . $requestException->getMessage() . PHP_EOL;
                $logger->appendLog('Error: ' . $requestException->getMessage());
                return 'Error: ' . $requestException->getMessage(); // Return early if the request fails
            }



        }


    } else if (preg_match('/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/', $dataToSearch)) {
        $clientIdentifierType = 'MAC';
        // La variable es una dirección MAC válida
        $logger->appendLog('Consulta por dirección MAC: ' . $dataToSearch);

        try {
            // //para mandarla al endpoint de dispositivos por MAC se necesita convertir la cadena de la direccion mac en un formato como el siguiente ejemplo: para la dirección mac 60:22:32:c8:b2:c3 quedaría como 60%3A22%3A32%3Ac8%3Ab2%3Ac3
            // $dataToSearch = str_replace(':', '%3A', $dataToSearch);
            // $logger->appendLog('Consulta por dirección MAC: ' . $dataToSearch );
            $responseDeviceByMAC = $clientUnms->request('GET', 'devices/mac/' . $dataToSearch, [
                'headers' => [
                    'X-Auth-Token' => $unmsAuthToken
                ]
            ]);

            if ($responseDeviceByMAC->getStatusCode() === 200) {

                $dataDeviceByMAC = json_decode($responseDeviceByMAC->getBody(), true);
                $jsonDeviceByMac = json_encode($dataDeviceByMAC, JSON_PRETTY_PRINT);
                // print_r($jsonDeviceByMac . PHP_EOL); //Devices por ID del sitio
                //$logger->appendLog($jsonDeviceByMac.PHP_EOL);

                //Ejemplo de $responseDeviceByMac en json: {"id":"84c8a581-154c-41db-81d5-a1b1c9ede411","type":"airMax","series":"AC"}
                //obtener el id del dispositivo y si no existe el id del dispositivo entonces devolver un mensaje de error
                if (isset($dataDeviceByMAC['id'])) {
                    $deviceId = $dataDeviceByMAC['id'];
                    // print_r('ID DEL DISPOSITIVO: ' . $deviceId . PHP_EOL); // ID del dispositivo
                } else {
                    // echo "No se encontró el dispositivo con la dirección MAC proporcionada." . PHP_EOL;
                    $logger->appendLog('No se encontró el dispositivo con la dirección MAC proporcionada: ' . $dataToSearch);
                    return 'Error: No se encontró el dispositivo con la dirección MAC proporcionada: ' . $dataToSearch; // Return early if no device is found
                }

            }

            $responsePasswordVault = $clientUnms->request('GET', 'vault/' . $deviceId . '/credentials', [
                'headers' => [
                    'X-Auth-Token' => $unmsAuthToken
                ]
            ]);

            if ($responsePasswordVault->getStatusCode() === 200) {
                $dataPasswordVault = json_decode($responsePasswordVault->getBody(), true);
                // $jsonPasswordVault = json_encode($dataPasswordVault, JSON_PRETTY_PRINT);
                //print_r($jsonPasswordVault . PHP_EOL); //Credenciales del dispositivo

                if (isset($dataPasswordVault['credentials'][0]['password'])) {
                    //asignar el campo password a la variable dataPasswordVault
                    $dataPasswordVault = $dataPasswordVault['credentials'][0]['password'];
                } else {
                    // echo "No se encontró la contraseña en la respuesta de la API." . PHP_EOL;
                    $logger->appendLog('No se encontró una contraseña en la bóveda para la antena de este cliente, es altamente probable que conserve una contraseña conocida.');
                    return "Error: No se encontró una contraseña en la bóveda para la antena de este cliente, es altamente probable que conserve una contraseña conocida."; // Return early if the password is not found
                }
                return $dataPasswordVault;

            } else {
                // echo "Error en la solicitud. Código de estado HTTP: " . $responsePasswordVault->getStatusCode() . PHP_EOL;
                $logger->appendLog('Error en la solicitud. Código de estado HTTP: ' . $responsePasswordVault->getStatusCode());
                return 'Error: Falla en la solicitud. Código de estado HTTP: ' . $responsePasswordVault->getStatusCode(); // Return early if the request fails
                
            }


        } catch (RequestException $requestException) {
            // Manejar errores de la solicitud, si hay error 404 entonces responder que no se encontró niguna MAC asociada a alguna antena o dispositivo de red
            if ($requestException->hasResponse()) {
                $response = $requestException->getResponse();
                $statusCode = $response->getStatusCode();
                $reason = $response->getReasonPhrase();
                // echo "Error: {$statusCode} - {$reason}" . PHP_EOL;
                // echo $response->getBody();
                if ($statusCode == 404) {
                    // echo "No se encontró el cliente con la dirección MAC proporcionada." . PHP_EOL;
                    $logger->appendLog('No se encontró ninguna antena de cliente o dispositivo en la red con la dirección MAC proporcionada: ' . $dataToSearch);
                    return 'Error: No se encontró ninguna antena de cliente o dispositivo en la red con la dirección MAC proporcionada: ' . $dataToSearch; // Return early if the client is not found
                }
                return 'Error: ' . $reason; // Return early if the request fails
            } else {
                // echo "Error: " . $requestException->getMessage() . PHP_EOL;
                $logger->appendLog('Error: ' . $requestException->getMessage());
                return 'Error: ' . $requestException->getMessage(); // Return early if the request fails
            }


        }


    } else {
        $clientIdentifierType = 'ID';
        // La variable no es una dirección IP válida, se asume que es un ID
        $IpAddressClientId = filter_var($dataToSearch, FILTER_SANITIZE_NUMBER_INT);
        //print ('Consulta por ID: ' . $dataToSearch . PHP_EOL);
        // $logger->appendLog('Consulta por ID' . $dataToSearch);
        // $logger->appendLog('endpoint que se envía: '.$ucrmBaseUri.'clients/services?clientId=' . $IpAddressClientId);
        // $logger->appendLog('apiToken que se usa: '.$crmAuthToken);

        try {
            //Obtener id del sitio por medio del servicio   
            $responseServices = $clientUcrm->get('clients/services?clientId=' . $IpAddressClientId, [
                'headers' => [
                    'X-Auth-Token' => $crmAuthToken,
                    'Content-Type: application/json'
                ]
            ]);

            //Obtener el nombre del cliente por medio del ID
            $responseClientName = $clientUcrm->get('clients/' . $IpAddressClientId, [
                'headers' => [
                    'X-Auth-Token' => $crmAuthToken,
                    'Content-Type: application/json'
                ]
            ]);
            //ejemplo de responseClientName en json: {"id":3201,"userIdent":null,"previousIsp":null,"isLead":false,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"CALLE CHIAPAS #31, ZONA CENTRO","street2":null,"city":"Dolores Hidalgo Cuna de la Independencia Nacional","countryId":173,"stateId":null,"zipCode":"37800","fullAddress":"CALLE CHIAPAS #31, ZONA CENTRO, Dolores Hidalgo Cuna de la Independencia Nacional, 37800","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":"Soy un teibolero :) programador en SIIP INTERNET","sendInvoiceByPost":null,"invoiceMaturityDays":17,"stopServiceDue":false,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2024-04-19T00:00:00-0600","leadConvertedAt":null,"companyContactFirstName":null,"companyContactLastName":null,"isActive":false,"firstName":"DANIEL HUMBERTO","lastName":"SOTO VILLEGAS","username":"dhsv.141089@gmail.com","contacts":[{"id":4188,"clientId":3201,"email":"dhsv.141089@gmail.com","phone":"5214181878106","name":null,"isBilling":false,"isContact":false,"types":[{"id":4,"name":"WhatsApp"}]},{"id":5242,"clientId":3201,"email":null,"phone":"5214181148783","name":"WA Bussiness","isBilling":false,"isContact":false,"types":[]}],"attributes":[{"id":21180,"clientId":3201,"customAttributeId":35,"name":"Site","key":"site","value":"0LOCS","clientZoneVisible":true},{"id":18147,"clientId":3201,"customAttributeId":1,"name":"ip","key":"ip","value":"172.16.13.64","clientZoneVisible":false},{"id":19859,"clientId":3201,"customAttributeId":29,"name":"Stripe Customer ID","key":"stripeCustomerId","value":"cus_RKiSN955qKhmch","clientZoneVisible":true},{"id":19860,"clientId":3201,"customAttributeId":30,"name":"Clabe Interbancaria","key":"clabeInterbancaria","value":"124180650741646979","clientZoneVisible":true},{"id":21181,"clientId":3201,"customAttributeId":36,"name":"Antena/Sectorial","key":"antenaSectorial","value":"Sectorial-4b 172.16.13.16/24","clientZoneVisible":true},{"id":27184,"clientId":3201,"customAttributeId":21,"name":"Chat de CallBell","key":"chatDeCallbell","value":"https://dash.callbell.eu/chat/90611886ac204c56a75240f951c53874","clientZoneVisible":false},{"id":28687,"clientId":3201,"customAttributeId":22,"name":"uuid","key":"uuid","value":"2038d2d99ae543f3b56f1ae54f4cc82b","clientZoneVisible":false},{"id":29853,"clientId":3201,"customAttributeId":37,"name":"Password Antena Cliente","key":"passwordAntenaCliente","value":"gYAIEK:Be}SK*01z5+/V","clientZoneVisible":false}],"accountBalance":-300,"accountCredit":0,"accountOutstanding":300,"currencyCode":"MXN","organizationName":"SIIP","bankAccounts":[{"id":2,"accountNumber":"124180650741646979"}],"tags":[{"id":1,"name":"NS EXENTO","colorBackground":"#00a0df","colorText":"#fff"}],"invitationEmailSentDate":null,"avatarColor":"#4caf50","addressGpsLat":21.1564209,"addressGpsLon":-100.9384185,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":true,"hasOutage":false,"hasSuspendedService":false,"hasServiceWithoutDevices":false,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false}

            $clientName = json_decode($responseClientName->getBody(), true);
            $clientFullName = $clientName['firstName'] . ' ' . $clientName['lastName'];
            $logger->appendLog('CLIENTE: ' . $clientFullName . ' (ID: ' . $IpAddressClientId . ')');

            




        } catch (RequestException $requestException) {
            // Manejar errores de la solicitud
            if ($requestException->hasResponse()) {
                $response = $requestException->getResponse();
                $statusCode = $response->getStatusCode();
                $reason = $response->getReasonPhrase();
                //si el statusCode es 404 significa que no se encontró el cliente
                if ($statusCode == 404) {
                    // echo "No se encontró el cliente con el ID proporcionado." . PHP_EOL;
                    $logger->appendLog('No se encontró el cliente con el ID proporcionado: ' . $IpAddressClientId);
                    return 'Error: No se encontró el cliente con el ID proporcionado: ' . $IpAddressClientId; // Return early if the client is not found
                }
                return 'Error: ' . $reason; // Return early if the request fails

            } else {
                // echo "Error: " . $requestException->getMessage() . PHP_EOL;
                $logger->appendLog('Error: ' . $requestException->getMessage());
                return 'Error: ' . $requestException->getMessage(); // Return early if the request fails
            }

        }

        if ($responseServices->getStatusCode() === 200) {

            $dataServices = json_decode($responseServices->getBody(), true);
            // $jsonServices = json_encode($dataServices, JSON_PRETTY_PRINT);
            // print_r($jsonServices . PHP_EOL);
            if (isset($dataServices[0])) {
                $unmsSiteID = $dataServices[0]['unmsClientSiteId']; // Example: 9c6798f3-0254-4e5b-bc3b-9da82fe16e46
            } else {
                // echo "No se encontraron servicios para el cliente proporcionado." . PHP_EOL;
                $logger->appendLog('No se encontraron servicios para el cliente proporcionado: ' . $IpAddressClientId);
                return "Error: No se encontraron servicios para el cliente proporcionado: " . $IpAddressClientId; // Return early if no services are found
            }

        } else {
            // echo "Error en la solicitud. Código de estado HTTP: " . $responseServices->getStatusCode() . PHP_EOL;
            $logger->appendLog('Error en la solicitud. Código de estado HTTP: ' . $responseServices->getStatusCode());
            return "Error: En la solicitud. Código de estado HTTP: " . $responseServices->getStatusCode();
        }
        // $logger->appendLog('ID del sitio obtenido: ' . $unmsSiteID);
        // $logger->appendLog('endpoint que se envía: '.$unmsBaseUri.'devices?siteId=' . $unmsSiteID);
        // $logger->appendLog('apiToken que se usa: '.$crmAuthToken);

        $responseDevicesBySite = $clientUnms->request('GET', 'devices?siteId=' . $unmsSiteID, [
            'headers' => [
                'X-Auth-Token' => $unmsAuthToken
            ]
        ]);



        if ($responseDevicesBySite->getStatusCode() === 200) {

            $dataDevicesBySite = json_decode($responseDevicesBySite->getBody(), true);
            $jsonDevicesBySite = json_encode($dataDevicesBySite, JSON_PRETTY_PRINT);
            //print_r($jsonDevicesBySite . PHP_EOL); //Devices por ID del sitio

            //id del device al que está conectado el cliente
            if (isset($dataDevicesBySite[0])) {
                $idClientDevice = $dataDevicesBySite[0]['identification']['id'];
                $deviceID = $dataDevicesBySite[0]['attributes']['apDevice']['id'];
            } else {
                // echo "No se encontraron dispositivos para el sitio proporcionado." . PHP_EOL;
                $logger->appendLog('No se encontraron dispositivos para el sitio proporcionado: ' . $unmsSiteID);
                return "Error: No se encontraron dispositivos para el sitio proporcionado."; // Return early if no devices are found
            }



        } else {
            // echo "Error en la solicitud. Código de estado HTTP: " . $responseDevicesBySite->getStatusCode() . PHP_EOL;
            $logger->appendLog('Error en la solicitud. Código de estado HTTP: ' . $responseDevicesBySite->getStatusCode());
            return "Error: Falla en la solicitud. Código de estado HTTP: " . $responseDevicesBySite->getStatusCode();
        }

        // $logger->appendLog('ID del device al que está conectado el cliente: ' . $deviceID);
        // $logger->appendLog('endpoint que se envía: '.$unmsBaseUri.'devices/' . $deviceID);
        // $logger->appendLog('apiToken que se usa: '.$unmsAuthToken);

        $responseDevicesBySite = $clientUnms->request('GET', 'devices/' . $deviceID, [
            'headers' => [
                'X-Auth-Token' => $unmsAuthToken
            ]
        ]);

        if ($responseDevicesBySite->getStatusCode() === 200) {

            $dataDevices = json_decode($responseDevicesBySite->getBody(), true);
            $jsonDevices = json_encode($dataDevices, JSON_PRETTY_PRINT);
            //print_r($jsonDevices . PHP_EOL);


            // $logger->appendLog('ID del device al que está conectado el cliente: ' . $idDevice);
            // $logger->appendLog('endpoint que se envía: '.$unmsBaseUri.'vault/' . $idDevice . '/credentials');
            // $logger->appendLog('apiToken que se usa: '.$unmsAuthToken);

            //print_r('ID del device al que está conectado el cliente: ' . $idDevice . PHP_EOL);
            $responsePasswordVault = $clientUnms->request('GET', 'vault/' . $idClientDevice . '/credentials', [
                'headers' => [
                    'X-Auth-Token' => $unmsAuthToken
                ]
            ]);


            if ($responsePasswordVault->getStatusCode() === 200) {
                $dataPasswordVault = json_decode($responsePasswordVault->getBody(), true);
                $jsonPasswordVault = json_encode($dataPasswordVault, JSON_PRETTY_PRINT);
                // $logger->appendLog($jsonPasswordVault); //Credenciales del dispositivo
                //ejemplo de $responsePasswordVault en json: {"credentials":[{"username":"ubnt","readOnly":false,"password":"gYAIEK:Be}SK*01z5+/V","createdAt":"2025-03-27T23:29:54.948Z"}],"isPassphraseMissing":false}

                //verificar si existe la contraseña en la respuesta de la API
                if (isset($dataPasswordVault['credentials'][0]['password'])) {
                    //asignar el campo password a la variable dataPasswordVault
                    $dataPasswordVault = $dataPasswordVault['credentials'][0]['password'];
                } else {
                    // echo "No se encontró la contraseña en la respuesta de la API." . PHP_EOL;
                    $logger->appendLog('Id device: ' . $idClientDevice);
                    $logger->appendLog('No se encontró una contraseña en la bóveda para la antena de este cliente, es altamente probable que conserve una contraseña conocida.');
                    return "Error: No se encontró una contraseña en la bóveda para la antena de este cliente, es altamente probable que conserve una contraseña conocida."; // Return early if the password is not found
                }
                return $dataPasswordVault;

            } else {
                // echo "Error en la solicitud. Código de estado HTTP: " . $responseDevicesBySite->getStatusCode() . PHP_EOL;
                $logger->appendLog('Error en la solicitud. Código de estado HTTP: ' . $responsePasswordVault->getStatusCode());
                return 'Error: Falla en la solicitud. Código de estado HTTP: ' . $responsePasswordVault->getStatusCode(); // Return early if the request fails
            }



        } else {
            // echo "Error en la solicitud. Código de estado HTTP: " . $responseDevicesBySite->getStatusCode() . PHP_EOL;
            $logger->appendLog('Error en la solicitud. Código de estado HTTP: ' . $responseDevicesBySite->getStatusCode());
            return 'Error: Falla en la solicitud. Código de estado HTTP: ' . $responseDevicesBySite->getStatusCode(); // Return early if the request fails

        }



    }
}

global $clientFullName, $IpAddressClientId; // Variables globales para usar en la función getVaultCredentials

/**
 * Genera una contraseña aleatoria y fuerte (CSPRNG).
 *
 * - Garantiza al menos 1 minúscula, 1 mayúscula, 1 dígito y 1 símbolo.
 * - Usa random_int() / random_bytes() (CSPRNG).
 * - Evita sesgos al seleccionar y mezcla la contraseña con Fisher-Yates.
 *
 * @param int $length Longitud deseada (mínimo 8). Default 20.
 * @param bool $avoid_ambiguous Si true evita caracteres ambiguos como l, I, 0, O.
 * @return string Contraseña generada.
 * @throws InvalidArgumentException Si $length < 8.
 */
function generate_strong_password(int $length = 20, bool $avoid_ambiguous = true): string
{
    if ($length < 8) {
        throw new InvalidArgumentException('La longitud mínima recomendada es 8 caracteres.');
    }

    // Conjuntos de caracteres
    $lower = 'abcdefghijklmnopqrstuvwxyz';
    $upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $digits = '0123456789';
    // Conjunto de símbolos seguros; puedes ampliarlo si lo deseas
    $symbols = '!@#$%&*()-_=+[]{};:,.<>?';

    if ($avoid_ambiguous) {
        // Eliminar caracteres ambiguos
        $amb = ['l', 'I', '1', '0', 'O'];
        $lower = str_replace($amb, '', $lower);
        $upper = str_replace($amb, '', $upper);
        $digits = str_replace($amb, '', $digits);
        // los símbolos raramente confunden, los mantenemos
    }

    // Asegurar al menos uno de cada tipo
    $required = [];
    $required[] = $lower[random_int(0, strlen($lower) - 1)];
    $required[] = $upper[random_int(0, strlen($upper) - 1)];
    $required[] = $digits[random_int(0, strlen($digits) - 1)];
    $required[] = $symbols[random_int(0, strlen($symbols) - 1)];

    // Relleno con una mezcla de todos los conjuntos
    $all = $lower . $upper . $digits . $symbols;
    $remaining = $length - count($required);
    $pwChars = $required;

    for ($i = 0; $i < $remaining; $i++) {
        $pwChars[] = $all[random_int(0, strlen($all) - 1)];
    }

    // Mezclar usando Fisher-Yates con random_int (sin sesgo)
    $n = count($pwChars);
    for ($i = $n - 1; $i > 0; $i--) {
        $j = random_int(0, $i);
        $tmp = $pwChars[$i];
        $pwChars[$i] = $pwChars[$j];
        $pwChars[$j] = $tmp;
    }

    return implode('', $pwChars);
}
?>

<!DOCTYPE html>
<html lang="es">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Buscador de Contraseñas de la bóveda para dispositivos de la red UISP</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
            background-color: #f4f4f9;
            color: #333;
        }

        h1 {
            color: #2955CD;
            text-align: center;
            margin-bottom: 30px;
        }

        form {
            max-width: 500px;
            margin: 0 auto;
            padding: 20px;
            background-color: #fff;
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }

        label {
            display: block;
            margin-bottom: 10px;
            font-weight: bold;
            color: #2955CD;
            text-align: center;
        }

        input[type="text"] {
            width: calc(100% - 20px);
            /* Margen de 10px en cada lado */
            padding: 10px;
            margin-bottom: 20px;
            border: 1px solid #ccc;
            border-radius: 4px;
            font-size: 16px;
            box-sizing: border-box;
            /* Asegura que el ancho incluya padding y borde */
        }

        button {
            display: block;
            /* Centra el botón */
            width: auto;
            /* Ancho automático */
            margin: 0 auto;
            /* Centrado horizontal */
            padding: 10px 20px;
            /* Tamaño acorde al texto */
            background-color: #52BFFF;
            color: white;
            font-size: 16px;
            font-weight: bold;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            transition: background-color 0.3s ease;
        }

        button:hover {
            background-color: #327BAC;
        }

        .resultado {
            max-width: 500px;
            margin: 20px auto;
            padding: 20px;
            background-color: #e6ffed;
            border: 1px solid #39cc64;
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }

        .resultado h3 {
            text-align: center;
            /* Centrar el texto del título */
            margin-bottom: 15px;
            color: #219e44;
            /* Color del texto en la caja de resultado */
        }

        .resultado h3.error {
            color: red;
            /* Color rojo para errores */
        }

        .caja-contrasena {
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        .caja-contrasena input {
            width: calc(100% - 22px);
            /* Ajuste para el padding y el borde */
            padding: 10px;
            margin-bottom: 15px;
            border: 1px solid #ccc;
            border-radius: 4px;
            font-size: 16px;
            color: #53535A;
            /* Color del texto en la caja de resultado */
            box-sizing: border-box;
            /* Asegura que el ancho incluya padding y borde */
        }

        .caja-contrasena button {
            padding: 10px 20px;
            background-color: #39cc64;
            color: white;
            font-size: 14px;
            font-weight: bold;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            transition: background-color 0.3s ease;
        }

        .caja-contrasena button:hover {
            background-color: #2ab653;
        }

        .error {
            color: red;
            font-weight: bold;
            text-align: center;
            margin-top: 10px;
        }
    </style>
</head>

<body>

    <h1>Buscador de contraseñas de dispositivos almacenados en la bóveda de UISP</h1>

    <!-- Formulario -->
    <form method="POST" action="">
        <label for="identificador">Ingrese el ID del cliente, dirección IP o dirección MAC de la antena del cliente,
            sectorial o AP:</label>
        <input type="text" id="identificador" name="identificador" required
            placeholder="Ejemplo: 3201 o 172.16.13.64 o 00:1A:2B:3C:4D:5E">
        <button type="submit">
            <img src="public.php?img=search-icon.png" alt="Obtener" style="width:16px; height:16px; vertical-align:middle; margin-right:5px;">Buscar contraseña
        </button>
    </form>


    <!-- Resultado -->
    <?php if ($respuestaAPI) { ?>
    <script>
        window.clientFullName = '<?php echo addslashes(!empty($clientFullName) ? $clientFullName : ''); ?>';
        window.IpAddressClientId = '<?php echo addslashes(!empty($IpAddressClientId) ? $IpAddressClientId : ''); ?>';
        window.clientIdentifierType = '<?php echo addslashes($clientIdentifierType); ?>';
    </script>
        <div class="resultado">
            <h3 class="<?php echo $esError ? 'error' : ''; ?>">
                <?php 
                $errorText = $esError ? htmlspecialchars(substr($respuestaAPI, 6)) : 'Contraseña obtenida';
                echo $errorText;
                ?>
            </h3>
            <?php 
            // Mostrar info de cliente si la contraseña fue encontrada
            if (!$esError) {
                $displayName = !empty($clientFullName) ? htmlspecialchars($clientFullName) : 'Cliente';
                $displayId = !empty($IpAddressClientId) ? htmlspecialchars($IpAddressClientId) : '';
?>
                <div style="text-align:center; color:#53535A; font-size:15px; margin-bottom:10px;">
                    <b><?php echo $displayName; ?></b> <?php if($displayId) { echo "(" . $clientIdentifierType . ": <b>" . $displayId . "</b>)"; } ?>
                </div>
            <?php }
            if ($esError && strpos($respuestaAPI, 'No se encontró una contraseña') !== false) { ?>
                <div id="generar-contenedor" style="text-align:center; margin-top:20px;">
                    <div style="margin-bottom:10px; color: #2c1ee9ff; font-size:16px;">¿Desea intentar generar una contraseña para la antena de este cliente?</div>
                    <button id="btn-generar" type="button" style="background: #04bb75ff; color:white; font-weight:bold; border:none; border-radius:4px; padding:10px 20px; font-size:15px; cursor:pointer;">
                        <img src="public.php?img=generate-password-icon.png" alt="Generar" style="width:16px; height:16px; vertical-align:middle; margin-right:5px;">Generar una contraseña
                    </button>
                </div>
                <script>
                document.getElementById('btn-generar').onclick = function() {
                    // Generar la contraseña y actualizar el atributo en el cliente
                    fetch('public.php?generate_password=1')
                        .then(response => response.text())
                        .then(password => {
                            document.getElementById('generar-contenedor').innerHTML = '';
                            let resultadoDiv = document.querySelector('.resultado');
                            resultadoDiv.querySelector('h3').textContent = 'Nueva contraseña generada';

                            // Mostrar info de cliente
                            let displayId = window.IpAddressClientId || '';
                            let displayName = window.clientFullName || 'Cliente';
                            let infoDiv = document.createElement('div');
                            infoDiv.style = 'text-align:center; color:#53535A; font-size:15px; margin-bottom:10px;';
                            infoDiv.innerHTML = `<b>${displayName}</b> ${displayId ? `(${window.clientIdentifierType || 'ID'}: <b>${displayId}</b>)` : ''}`;
                            resultadoDiv.appendChild(infoDiv);

                            let caja = document.createElement('div');
                            caja.className = 'caja-contrasena';
                            caja.innerHTML = `<input type="text" id="contrasena" value="${password}" readonly>
                                <button onclick="copiarAlPortapapeles()">
                                    <img src='public.php?img=clipboard-icon.png' alt='Copiar' style='width:16px; height:16px; vertical-align:middle; margin-right:5px;'>Copiar Contraseña
                                </button>`;
                            resultadoDiv.appendChild(caja);

                            // Llamar al endpoint para actualizar la contraseña en el cliente
                            if (displayId) {
                                fetch('public.php', {
                                    method: 'POST',
                                    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                                    body: `update_password=1&clientId=${encodeURIComponent(clientId)}&password=${encodeURIComponent(password)}`
                                })
                                .then(res => res.json())
                                .then(data => {
                                    alert(data.success ? 'Contraseña actualizada' : 'Error: ' + data.message);
                                });
                            }
                        });
                };
                </script>
            <?php } else if (!$esError) { ?>
                <div class="caja-contrasena">
                    <input type="text" id="contrasena" value="<?php echo htmlspecialchars($respuestaAPI); ?>" readonly>
                    <button onclick="copiarAlPortapapeles()">
                        <img src="public.php?img=clipboard-icon.png" alt="Copiar" style="width:16px; height:16px; vertical-align:middle; margin-right:5px;">Copiar Contraseña
                    </button>
                </div>
            <?php } ?>
        </div>
    <?php } ?>

    <script>
        // Función para copiar la contraseña al portapapeles
        function copiarAlPortapapeles() {
            const contrasenaInput = document.getElementById('contrasena');
            contrasenaInput.select();
            contrasenaInput.setSelectionRange(0, 99999); // Para dispositivos móviles
            document.execCommand('copy');
            alert('Contraseña copiada al portapapeles: ' + contrasenaInput.value);
        }
    </script>

</body>

</html><q></q>