<?php
namespace App\Http\Controllers;

use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Request;
use Carbon\Carbon;
use App\Http\Controllers\IAController;
use App\Custom\SQLConnection;
use App\Custom\Misc;
use DateTime;
use stdClass;
use DB;
use Log;
use Storage;
use Throwable;

class ApiDashboardController extends Controller {


    // --- Método para la carga inicial de la vista ---
    public static function indexDashboardApi_Json() {
        // Definir filtros por defecto para la carga inicial: MES ACTUAL
        $defaultFilters = [
            'periodo' => 'mensual',   // <--- CAMBIADO a 'mensual'
            'mes' => date('n'),     // <--- Correcto: Mes actual (1-12)
            'dia' => null,          // <--- CAMBIADO a null (no aplica para mensual)
            'producto' => null,     // 'todos'
            'cliente' => null,      // 'todos'
        ];

        $initialData = self::getDashboardDataFromFile($defaultFilters); // Usará la instancia actual si no es estático
        Carbon::setLocale('es');
        if (!$initialData) {
            Log::warning("No se pudieron obtener datos iniciales del archivo para el dashboard API (Mes Actual).");
            $currentMonthName = Carbon::now()->monthName; // Nombre del mes actual
            $initialData = [
                'metadata' => [
                    'mesConsultado' => "Datos no disp. ($currentMonthName)", // Indica el mes intentado
                    'subtitulo' => 'Mes Actual',
                    'periodo' => $defaultFilters['periodo']
                ],
                'cards' => [],
                'graphs' => [],
            ];
            // Poblar con claves de gráficos vacías...
            $expectedGraphs = ['solicitudesPeriodo', 'tasaAciertosErroresPie', /* ... */ 'distribucionTiempos'];
            foreach ($expectedGraphs as $graphKey) {
                $initialData['graphs'][$graphKey] = ['categories' => [], 'series' => []];
                if ($graphKey === 'tasaAciertosErroresPie') {
                    $initialData['graphs'][$graphKey] = ['seriesData' => []];
                }
            }
        }

        $clientesApis = SQLConnection::backendStoreProcedure('getClientesDashboardApi', []);
        $productosApis = SQLConnection::backendStoreProcedure('getProductosDashboardApi', []);


        usort($clientesApis, function ($a, $b) {
            return strcasecmp($a->nombre_cliente, $b->nombre_cliente);
        });

        usort($productosApis, function ($a, $b) {
            return strcasecmp($a->nombre_producto, $b->nombre_producto);
        });

        $ultimaConsultaProvedores = SQLConnection::backendStoreProcedure('fechas_consultas_API',[]);
       
       

        $ultimaConsultaProvedoresArg = [];

        foreach ($ultimaConsultaProvedores as $consulta) {
            $ultimaConsultaProvedoresArg[] = json_decode(json_encode([
                'computed' => $consulta->proveedor,
                'computed1' => Carbon::parse($consulta->fecha)->format('d/m/Y H:i:s'),
            ]));
        }
       
        return view('dashboard-api/dashboard_new')->with(compact([
             'initialData','clientesApis','productosApis','ultimaConsultaProvedoresArg'
        ]));
    }

   



    public static function getDashboardDataFromFile(array $filters)
    {
        $productoId = $filters['producto'] ?? null;
        $clienteId = $filters['cliente'] ?? null;
   
        // Determinar el rango de fecha
        if ($filters['periodo'] === 'diario' && !empty($filters['dia'])) {
            $rangoFecha = $filters['dia']; // Formato esperado: YYYY-MM-DD
        } elseif ($filters['periodo'] === 'mensual' && !empty($filters['mes'])) {
            $año = $filters['anio'] ?? date('Y');
            $rangoFecha = $año . '-' . str_pad($filters['mes'], 2, '0', STR_PAD_LEFT); // YYYY-MM
        } else {
            Log::warning("Período o fecha no especificados claramente para buscar archivo.", $filters);
            return null;
        }
   
        try {
            $baseDir = env('API_DASHBOARD_JSON_DIR');
   
            // Uso mismas funciones que use en DashboardJSONController (desde Misc)
            $rutaCarpeta = Misc::generarRutaCarpeta($rangoFecha, $clienteId, $productoId, $baseDir);
            $filename = Misc::generarNombreArchivo($productoId, $clienteId, $rangoFecha);
            $filePath = "{$rutaCarpeta}/{$filename}.json";
            //dd($filePath);
            $datos = null;
            if (file_exists($filePath)) {
                $file_get = file_get_contents($filePath);
                if ($file_get !== false) {
                    $datos = json_decode($file_get, true);
                } else {
                    Log::error("No se pudo leer el archivo JSON: " . $filePath);
                }
            } else {
                Log::warning("Archivo JSON no encontrado: " . $filePath);
            }
            return $datos;

        } catch(\Exception $e) {
            Log::error("Error al procesar archivo JSON: " . $e->getMessage(), $filters);
            return null;
        }
    }

    // --- filtrarDashboardApi (método AJAX) ---
    // No necesita cambios directos aquí, ya que recibe los filtros del JS
    // y llama a getDashboardDataFromFile que ya tiene la lógica correcta.
    public function filtrarDashboardApi(Request $request)
    {
        $filters = [
            'periodo' => request::input('periodo', 'mensual'),
            'mes' => request::input('mes'),
            'anio' => request::input('anio'),
            'dia' => request::input('dia'),
            'producto' => request::input('producto'),
            'cliente' => request::input('cliente'),
        ];
        //dd($filters);
            $filteredData = self::getDashboardDataFromFile($filters);
            //dd($filteredData);
            if (!$filteredData) {
                Log::warning("No se encontraron datos de archivo para filtros AJAX:", $filters);
       
                $periodo = $filters['periodo'] ?? 'mensual';
                $subtitulo = ($periodo === 'diario') ? "Día seleccionado" : "Mes seleccionado";
       
                // Estructura vacía para respuesta coherente
                $emptyData = [
                    'metadata' => [
                        'mesConsultado' => "Sin datos disponibles",
                        'subtitulo' => $subtitulo,
                        'periodo' => $periodo,
                    ],
                    'cards' => [],
                    'graphs' => [],
                ];
       
                $expectedGraphs = [
                    'solicitudesPeriodo',
                    'tasaAciertosErroresPie',
                    'codigosRespuesta',
                    'tiempoPromedioProcesamiento',
                    'erroresPorCodigo',
                    'erroresPorCliente',
                    'erroresPorProducto',
                    'clientesConMayorUso',
                    'productosConMayorUso',
                    'tasaAciertosErroresBarra',
                    'distribucionTiempos'
                ];
       
                foreach ($expectedGraphs as $graphKey) {
                    if ($graphKey === 'tasaAciertosErroresPie') {
                        $emptyData['graphs'][$graphKey] = ['seriesData' => []];
                    } else {
                        $emptyData['graphs'][$graphKey] = ['categories' => [], 'series' => []];
                    }
                }
       
                return response()->json($emptyData, 200);
            }
        //  sleep(3);
        return response()->json($filteredData);
    }
     

    public static function ActualDashboardApi(){
        // dd('llego');

       
        $productoId = request::input('producto');
        $clienteId = request::input('cliente');


        $paramsParaSpDirectos = [$productoId, $clienteId];
       
        // Para los SPs envueltos por BackendDashboardApi, la acción va primero en el array de SQLConnection
        $paramsPayloadParaBackendApi = [$productoId, $clienteId];
        // dd($paramsPayloadParaBackendApi);

        //dd($paramsPayloadParaBackendApi);
        // ----- LLAMADAS A STORE PROCEDURES -----

        $rawSolicitudesTotales = SQLConnection::backendStoreProcedure('backendDashboardApiActual', array_merge(['GET_TOTAL_CONSULTAS'], $paramsParaSpDirectos));
        $solicitudesTotales = $rawSolicitudesTotales[0] ?? null;

        $rawTiempoPromedioResp = SQLConnection::backendStoreProcedure('backendDashboardApiActual', array_merge(['GET_TIEMPO_PROMEDIO'], $paramsPayloadParaBackendApi));
        $tiempoPromedioResp = $rawTiempoPromedioResp[0] ?? null;
       
        $rawTiempoMayorMenor = SQLConnection::backendStoreProcedure('backendDashboardApiActual', array_merge(['GET_TIEMPO_MAYOR_MENOR'], $paramsPayloadParaBackendApi));
        $tiempoMayorMenor = $rawTiempoMayorMenor[0] ?? null;

        $rawPromedioConsumoApi = SQLConnection::backendStoreProcedure('backendDashboardApiActual', array_merge(['GET_PROMEDIO_CONSUM_PROD'], $paramsPayloadParaBackendApi));
        $promedioConsumoApi = $rawPromedioConsumoApi[0] ?? null;
                   
        $rawPromedioConsumoCliente = SQLConnection::backendStoreProcedure('backendDashboardApiActual', array_merge(['GET_PROMEDIO_CONSUM_CLIENT'], $paramsPayloadParaBackendApi));
        $promedioConsumoCliente = $rawPromedioConsumoCliente[0] ?? null;

        $totalConsultasClienteGraf = SQLConnection::backendStoreProcedure('backendDashboardApiActual', array_merge(['GET_CONSULTAS_CLIENTE_GRAF'], $paramsPayloadParaBackendApi));
 
        $totalConsultasProductGraf = SQLConnection::backendStoreProcedure('backendDashboardApiActual', array_merge(['GET_CONSULTAS_PRODUCT_GRAF'], $paramsPayloadParaBackendApi));
       
        $dataCodigoRespuesta = SQLConnection::backendStoreProcedure('backendDashboardApiActual', array_merge(['GET_CODIGO_RESP_DATA'], $paramsPayloadParaBackendApi));
   
        $tiempoPromedioProduct = SQLConnection::backendStoreProcedure('backendDashboardApiActual', array_merge(['GET_TIEMPO_PRODUCT'], $paramsPayloadParaBackendApi));
   
        $rangoTiempos = SQLConnection::backendStoreProcedure('backendDashboardApiActual', array_merge(['GET_RANGO_TIEMPO'], $paramsPayloadParaBackendApi));
   
        $apisMasErrores = SQLConnection::backendStoreProcedure('backendDashboardApiActual', array_merge(['GET_APIS_MAS_ERRORES'], $paramsPayloadParaBackendApi));
   
        $totalPromedioRespuestaGraf = SQLConnection::backendStoreProcedure('backendDashboardApiActual', array_merge(['GET_TOTAL_CONSULTAS_GRAF'], $paramsPayloadParaBackendApi));
   
        // $codResultAnual = SQLConnection::backendStoreProcedure('api_TasaErrores', $paramsParaSpDirectos);
        $codResultAnual = SQLConnection::backendStoreProcedure('backendDashboardApiActual', ['GET_COD_RESULTADOS_AN']);
       
        $tiempoPromedioAn = SQLConnection::backendStoreProcedure('backendDashboardApiActual', array_merge(['GET_TIEMPO_PROM_AN']), $paramsParaSpDirectos);

        //Tiempo procesamiento para actual
        $periodo = 'actual';
        $tiempoProce = SQLConnection::backendStoreProcedure('api_promedioTiempoProcesamiento', [$periodo, null, $productoId, $clienteId]);
        $tiemposDetallados = [];

        //Paso $tiempoProce de array a colección, esto hace que sea mas facil agregar contenidos al json en caso de cambiar SP
        foreach ($tiempoProce as $item) {
            // Normalizamos el nombre del proveedor como clave para que coincida con clases de la blade
            $key = strtolower(str_replace(' ', '_', $item->proveedor ?? 'global'));

            // Armamos el array base
            $tiemposDetallados[$key] = [
                'duracion' => $item->duracion !== null ? (float) $item->duracion : null,
            ];

            // Se pasa nombres solo a primera key porque es la unica que los tendria
            if ($key === 'promedio_total_apis') {
                $tiemposDetallados[$key]['nombre_cliente'] = $item->nombre_cliente ?? null;
                $tiemposDetallados[$key]['nombre_producto'] = $item->nombre_producto ?? null;
            } else {
                $tiemposDetallados[$key]['proveedor'] = $item->proveedor;
            }
        }
        // ----- PROCESAMIENTO DE DATOS PARA GRÁFICOS (Adaptado de tu código) -----
           
        // GRAFICO: Consumo por Cliente
        $nombre_cliente_graf = [];
        $consumo_total_graf = [];
        if(!empty($totalConsultasClienteGraf)){
            foreach ($totalConsultasClienteGraf as $dato) {
                $nombre_cliente_graf[] = $dato->nombre_cliente ?? 'N/A';
                $consumo_total_graf[]= (float)($dato->consumo_total ?? 0);
            }
        }

        // GRAFICO: Consumo por Producto/API
        $producto_graf = [];
        $total_registros_graf = [];
        if (!empty($totalConsultasProductGraf)) {
            foreach ($totalConsultasProductGraf as $dato) {
                $producto_graf[] = $dato->nombre_producto ?? 'N/A';
                $total_registros_graf[]= (float)($dato->total_registros ?? 0);
            }
        }

        // GRAFICO: Códigos de Respuesta
        $codigo_resultado_graf = [];
        $cantidad_Respuesta_graf = [];
        if (!empty($dataCodigoRespuesta)) {
            foreach ($dataCodigoRespuesta as $dato) {
                $codigo_resultado_graf[] = (string)($dato->codigo ?? 'N/A');
                $cantidad_Respuesta_graf[]= (float)($dato->cantidad ?? 0);
            }
        }
           
        // GRAFICO: APIs más Lentas
        $tiempo_api_resp_graf = [];
        $nombre_api_graf = [];
        if(!empty($tiempoPromedioProduct)){
            foreach ($tiempoPromedioProduct as $dato) {
                $tiempo_api_resp_graf[] = (float)($dato->promedio_duracion ?? 0.0);
                $nombre_api_graf[]= $dato->nombre_producto ?? 'N/A';
            }
        }
           
        // GRAFICO: Distribución de Tiempos
        $rango_tiempo_graf = [];
        $cantidad_Tiempo_graf = [];
        if(!empty($rangoTiempos)){
            foreach ($rangoTiempos as $dato) {
                $rango_tiempo_graf[] = $dato->rango_tiempo ?? 'N/A';
                $cantidad_Tiempo_graf[]= (float)($dato->cantidad ?? 0);
            }
        }
           
        // GRAFICO: APIs con más Errores
        $nombre_producto_errores_graf = [];
        $series_errores_graf = [];
        $tipos_error_keys = [ // Definir los tipos de error y las propiedades correspondientes del objeto $dato
            '307' => 'error_307', '306' => 'error_306', 'Internal Error' => 'internal_error',
            'Internal E' => 'internal_e', 'Error General' => 'error_general',
            'Error Producto' => 'error_producto', '400' => 'error_400'
        ];
        $errores_por_tipo_tmp = [];
       
        if(!empty($apisMasErrores)){
            foreach (array_keys($tipos_error_keys) as $key) {
                $errores_por_tipo_tmp[$key] = []; // Inicializa un array para cada tipo de error
            }
            foreach ($apisMasErrores as $dato) {
                $nombre_producto_errores_graf[] = $dato->nombre_producto ?? 'N/A';
                foreach ($tipos_error_keys as $displayName => $property) {
                    $errores_por_tipo_tmp[$displayName][] = (int)($dato->{$property} ?? 0);
                }
            }
            foreach($errores_por_tipo_tmp as $name => $data_errores) { // Renombré $data a $data_errores
                if (!empty($data_errores)) { // Solo añadir series si tienen datos
                    $series_errores_graf[] = ['name' => $name, 'data' => $data_errores];
                }
            }
        }
        // Asegurarse que $nombre_producto_errores_graf tenga la misma longitud que los 'data' en $series_errores_graf
        // Si $apisMasErrores estaba vacío, $nombre_producto_errores_graf también lo estará, lo cual es correcto.
       
        // GRAFICO: Solicitudes OK/Error en el Periodo
        $categorias_periodo_graf = [];
        $aciertos_periodo_graf = [];
        $errores_periodo_graf = [];
        //dd($tiempoPromedioAn);
        if(!empty($totalPromedioRespuestaGraf)){
            foreach ($totalPromedioRespuestaGraf as $dato) {
                // Asumiendo que $dato->mes es YYYY-MM para mensual
                $categorias_periodo_graf[] = Carbon::createFromFormat('Y-m', $dato->mes)->translatedFormat('M');
                $aciertos_periodo_graf[] = (int)($dato->total_ok ?? 0);
                $errores_periodo_graf[] = (int)($dato->total_error ?? 0) ;
            }
        }
        //GRAFICO: TIEMPO DE PROCESAMIENTO
        $mes_tiempoPromedio_categorias = [];
        $promedio_duracion_data = [];
        if(!empty($tiempoPromedioAn)){
            foreach ($tiempoPromedioAn as $dato){
                $mes_tiempoPromedio_categorias[] =(int)($dato->hora ?? 0);
                $promedio_duracion_data[] = (float)($dato->promedio_duracion ?? 0.0);
            }

        }
        // GRAFICO: Tasa de Errores por Código en el Periodo
        // $campoFecha es la columna en $codResultAnual que tiene la fecha ('dia' o 'mes')
        $campoFechaUsar = 'hora'; // La columna que contiene la hora (0, 8, etc.)
        $agrupado_tasa_errores_graf = [];
        $series_tasa_errores_graf = [];

        if (!empty($codResultAnual)) {
            // 1. Obtener todas las horas únicas y formatearlas para las categorías del gráfico
            $agrupado_tasa_errores_graf = collect($codResultAnual)
                ->pluck($campoFechaUsar) // Obtiene todas las 'hora'
                ->map(function ($hora) {
                    // Formatear la hora como "HH:00"
                    return str_pad((string)$hora, 2, '0', STR_PAD_LEFT) . ':00';
                })
                ->unique()
                ->sort() // Ordenar las horas cronológicamente
                ->values()
                ->all();
               
            // 2. Obtener todos los códigos de resultado únicos
            $codigos = collect($codResultAnual)
                ->pluck('codigo_resultado')
                ->unique()
                ->filter() // filter() para quitar nulos/vacíos
                ->values()
                ->all();

            // 3. Inicializar la estructura para almacenar errores por código y por hora formateada
            $erroresPorHoraYCodigo = [];
            foreach ($codigos as $codigo) {
                // Usar las horas formateadas como claves
                $erroresPorHoraYCodigo[(string)$codigo] = array_fill_keys($agrupado_tasa_errores_graf, 0);
            }
           
            // 4. Llenar la estructura con los totales de error
            foreach ($codResultAnual as $item) {
                $horaItem = $item->{$campoFechaUsar}; // Obtener la hora del item (ej. 0, 8)
                // Formatear la hora del item de la misma manera que las categorías
                $horaFormateada = str_pad((string)$horaItem, 2, '0', STR_PAD_LEFT) . ':00';
               
                $codigo = (string)($item->codigo_resultado ?? 'N/A');

                // Verificar que el código y la hora formateada existan en nuestra estructura
                if (isset($erroresPorHoraYCodigo[$codigo]) && array_key_exists($horaFormateada, $erroresPorHoraYCodigo[$codigo])) {
                    $erroresPorHoraYCodigo[$codigo][$horaFormateada] += (int)($item->total_error ?? 0);
                }
            }

            // 5. Preparar las series para el gráfico
            foreach ($erroresPorHoraYCodigo as $codigo => $data_errores_por_hora) {
                $series_tasa_errores_graf[] = [
                    'name' => $codigo, // El nombre de la serie es el código de error
                    'data' => array_values($data_errores_por_hora), // Los valores de error para cada hora
                ];
            }
        }

        // ----- METADATA -----

        $ahora = Carbon::now();
        $horaParaSubtitulo = Carbon::now()->subHour(); // Resta una hora a la hora actual

        // Formato para "Datos al DD de MES de YYYY HH:MM"
        $mesConsultadoStr = "Datos al " . $ahora->translatedFormat('j \d\e F \d\e Y H:i');
           
        // Subtítulo con la hora anterior
        $subtitulo = 'DATOS DESDE LAS ' . $horaParaSubtitulo->format('H:00') . ' HASTA LAS ' . $ahora->format('H:00');
        // O si prefieres un rango más explícito incluyendo minutos:
        // $subtitulo = 'RANGO DE DATOS: ' . $horaParaSubtitulo->format('H:i') . ' - ' . $ahora->format('H:i');
        // O si simplemente quieres indicar que los datos son de "la última hora hasta ahora":
        // $subtitulo = 'DATOS DE LA ÚLTIMA HORA (DESDE ' . $horaParaSubtitulo->format('H:i') . ')';

   
        $metadataPeriodo = 'actual'; // Para la propiedad 'periodo' en metadata

        if ($productoId) {
            $subtitulo .= " - Producto: $productoId";
        }
        if ($clienteId) {
            $subtitulo .= " - Cliente: $clienteId";
        }

        // Si se filtra por producto o cliente, podrías añadirlo al subtitulo
        // if($productoId) $subtitulo .= " - Prod: $productoId";
        // if($clienteId) $subtitulo .= " - Cli: $clienteId";


        /**esto se va a borrar  */
        // --- INICIO DE DATOS HARDCODEADOS PARA EL HEATMAP --
        $datosUptimeHeatmap = [
            'monthName' => '',
            'daysInMonth' => '',
            'services' => [
                [
                    'name' => 'FIDELITAS',
                    'overallUptime' => '',
                    'dailyData' => ''
                ],
                [
                    'name' => 'RENAPER',
                    'overallUptime' => '',
                    'dailyData' => ''
                ],
                [
                    'name' => 'ARCA',
                    'overallUptime' => '',
                    'dailyData' => ''
                ]
            ]
        ];
        // ----- CONSTRUIR EL JSON FINAL -----
        $resultado = [
            'metadata' => [
                'mesConsultado' => $mesConsultadoStr, // Este puede seguir siendo la hora exacta de la consulta
            'subtitulo' => $mesConsultadoStr,           // Este es el que modificamos
            'periodo' => $metadataPeriodo,
            ],
            'cards' => [
                'solicitudes' => [
                    'total' => (int)($solicitudesTotales->cantidad_total ?? 0),
                    'ok' => (int)($solicitudesTotales->total_ok ?? 0),
                    'error' => (int)($solicitudesTotales->total_error ?? 0),
                ],
                'Ultrenaper' => '',
                'Ultarca' => '',
                'tiempoPromedioSegundos' => (float)($tiempoPromedioResp->segundos ?? 0.0), // Asumiendo que la propiedad es 'segundos'
                'tiemposExtremos' => [
                'min' => isset($tiempoMayorMenor->duracion_minima) ? (float)$tiempoMayorMenor->duracion_minima : null,
                'max' => isset($tiempoMayorMenor->duracion_maxima) ? (float)$tiempoMayorMenor->duracion_maxima : null,
                ],
                'consumoPromedio' => [
                    'porApi' => (float)($promedioConsumoApi->promedio_por_api ?? 0.0),
                    'porCliente' => (float)($promedioConsumoCliente->promedio_por_cliente ?? 0.0),
                ],
                'tiemposDetallados' => $tiempoProce ? $tiemposDetallados : null,
            ],
            'graphs' => [
                'solicitudesPeriodo' => [
                'categories' => $categorias_periodo_graf,
                'series' => [
                    [ 'name' => 'Aciertos', 'data' => $aciertos_periodo_graf ],
                    [ 'name' => 'Errores', 'data' => $errores_periodo_graf ],
                ]
                ],
                'tasaAciertosErroresPie' => [ // Para #TortaPorcentaje
                    'seriesData' => [
                    ['name' => 'Aciertos', 'y' => (int)($solicitudesTotales->total_ok ?? 0) ],
                    ['name' => 'Errores',  'y' => (int)($solicitudesTotales->total_error ?? 0) ],
                    ]
                ],
                'consumoPorApi' => [ // Para #apisConsumo
                    'categories' => $producto_graf,
                    'series' => [ ['name' => 'Consumos', 'data' => $total_registros_graf ] ]
                ],
                'consumoPorCliente' => [ // Para #clientesConsumo
                    'categories' => $nombre_cliente_graf,
                    'series' => [ ['name' => 'Consumos', 'data' => $consumo_total_graf ] ]
                ],
                'tasaErroresPorCodigo' => [ // Para #TasadeErrores
                    'categories' => $agrupado_tasa_errores_graf,
                    'series' => $series_tasa_errores_graf,
                ],
                'apisConMasErrores' => [ // Para #ApimasErrores
                    'categories' => $nombre_producto_errores_graf, // Nombres de productos
                    'series' => $series_errores_graf, // Array de {name: 'TipoError', data: [...]}
                ],  
                // ----- TIEMPO PROCESAMIENTO GRAPH - CORRECTED STRUCTURE -----
                'tiempoProcesamiento' => [ // para #TiempoProcesamiento
                    'categories' => $mes_tiempoPromedio_categorias,
                    'series' => [
                        // The series should be an array of objects, each with 'name' and 'data'
                        ['name' => 'Tiempo Promedio (s)', 'data' => $promedio_duracion_data]
                        // Adjust 'Tiempo Promedio (s)' if unit or name is different
                    ]
                ],
                    'codigosRespuesta' => [ // Para #CodigosResp
                        'categories' => $codigo_resultado_graf,
                        'series' => [ ['name' => 'Cantidad', 'data' => $cantidad_Respuesta_graf] ]
                    ],
                    'apisMasLentas' => [ // Para #ApismasLentas
                        'categories' => $nombre_api_graf,
                        'series' => [ ['name' => 'Segundos', 'data' => $tiempo_api_resp_graf] ]
                    ],
                    'distribucionTiempos' => [ // Para #DistriProce
                        'categories' => $rango_tiempo_graf,
                        'series' => [ ['name' => 'Cantidad', 'data' => $cantidad_Tiempo_graf] ]
                    ],
                'uptimeHeatmap' => $datosUptimeHeatmap
            ]
        ];

       
        return json_encode($resultado);
    }



    /*
        Que exista, despues se mejora - TB
    */

    public static function indexDashboardApi() {

        // Esto lo hacemos asi poque lo necesitan YA, despues se va mejorar el tema del store y los calculos - TB        
        $tiempoPromedioResp = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TIEMPO_PROMEDIO']);
        $solicitudesTotales = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TOTAL_CONSULTAS']);
        $totalPromedioRespuestaGraf = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TOTAL_CONSULTAS_GRAF']);
        $totalConsultasProductGraf = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_CONSULTAS_PRODUCT_GRAF']);
        $totalConsultasClienteGraf = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_CONSULTAS_CLIENTE_GRAF']);
        $dataCodigoRespuesta = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_CODIGO_RESP_DATA']);
        $tiempoPromedioAn = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TIEMPO_PROM_AN']);
        $tiempoPromedioProduct = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TIEMPO_PRODUCT']);
        $tiempoMayorMenor = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TIEMPO_MAYOR_MENOR']);
        $rangoTiempos = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_RANGO_TIEMPO']);
        // $codResultAnual = SQLConnection::backendStoreProcedure('api_TasaErrores',[]);
        $codResultAnual = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_COD_RESULTADOS_AN']);
        $apisMasErrores = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_APIS_MAS_ERRORES']);
        $promedioConsumoApi = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_PROMEDIO_CONSUM_PROD']);
        $promedioConsumoCliente = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_PROMEDIO_CONSUM_CLIENT']);

        $clientesApis = SQLConnection::backendStoreProcedure('getClientesDashboardApi', []);
        $productosApis = SQLConnection::backendStoreProcedure('getProductosDashboardApi', []);

        $tiempoProce = SQLConnection::backendStoreProcedure('api_promedioTiempoProcesamiento', []);

        //  $tiempoProce =  SQLConnection::backendStoreProcedure('api_promedioTiempoProcesamiento',['mensual',3, 92, 4405537]);

        //dd($tiempoProce);

        usort($clientesApis, function ($a, $b) {
            return strcasecmp($a->nombre_cliente, $b->nombre_cliente);
        });

        usort($productosApis, function ($a, $b) {
            return strcasecmp($a->nombre_producto, $b->nombre_producto);
        });


        $mesConsultado = $tiempoPromedioResp[0]->mes_consultado ?? NULL;
        $tiempoPromedioResp = $tiempoPromedioResp[0]->segundos ?? 0;
        $solicitudesTotales = $solicitudesTotales[0];
        $mesboton = $mesConsultado;
        /** reestructuramos el total para meses, errores, ok, y totales */
        $categorias = [];
        $aciertos = [];
        $errores = [];
        $totales = [];
        $total_aciertos = 0;
        $total_errores = 0;

        foreach ($totalPromedioRespuestaGraf as $dato) {
            $mesNombre = \Carbon\Carbon::createFromFormat('Y-m', $dato->mes)->translatedFormat('M'); // Mes abreviado (Ene, Feb, etc.)
            $categorias[] = $mesNombre;
            $aciertos[] = $dato->total_ok;
            $errores[] = $dato->total_error;
            $totales[] = $dato->total_consumos;
        }
        // dd($totalPromedioRespuestaGraf);
        foreach ($aciertos as $acierto) {
            $total_aciertos += $acierto; // Suma de los aciertos
        }

        foreach ($errores as $error) {
            $total_errores += $error; // Suma de los errores

        }

        /**reestructuramos para totalConsultasProductGraf  */
        $producto = [];
        $total_registros = [];
        foreach ($totalConsultasProductGraf as $dato) {
            $producto[] = $dato->nombre_producto;
            $total_registros[] = $dato->total_registros;
        }

        /**reestructuramos para totalConsultasClienteGraf */
        $nombre_cliente = [];
        $consumo_total = [];
        foreach ($totalConsultasClienteGraf as $dato) {
            $nombre_cliente[] = $dato->nombre_cliente;
            $consumo_total[] = $dato->consumo_total;
        }

        /**reestructuramos para tiempoPromedioAn */
        $mes_tiempoPromedio = [];
        $promedio_duracion = [];
        foreach ($tiempoPromedioAn as $dato) {
            $mes_tiempoPromedio[] = \Carbon\Carbon::createFromFormat('Y-m', $dato->mes)->translatedFormat('M');
            $promedio_duracion[] = $dato->promedio_duracion;
        }


        /**reestructuramos para dataCodigoRespuesta */
        $codigo_resultado = [];
        $cantidad_Respuesta = [];
        foreach ($dataCodigoRespuesta as $dato) {
            $codigo_resultado[] = $dato->codigo;
            $cantidad_Respuesta[] = $dato->cantidad;
        }

        $tiempo_api_resp = [];
        $nombre_api = [];
        foreach ($tiempoPromedioProduct as $dato) {
            $tiempo_api_resp[] = $dato->promedio_duracion;
            $nombre_api[] = $dato->nombre_producto;
        }

        /**reestructuramos para rangoTiempos */
        $rango_tiempo = [];
        $cantidad_Tiempo = [];
        foreach ($rangoTiempos as $dato) {
            $rango_tiempo[] = $dato->rango_tiempo;
            $cantidad_Tiempo[] = $dato->cantidad;
        }

        $agrupado = collect($codResultAnual)
            ->pluck('mes')
            ->map(fn($mes) => \Carbon\Carbon::createFromFormat('Y-m', $mes)->format('M'))
            ->unique()
            ->values();

        $codigos = collect($codResultAnual)
            ->pluck('codigo_resultado')
            ->unique()
            ->values();

        $erroresPorMesYCodigo = [];

        foreach ($agrupado as $mes) {
            foreach ($codigos as $codigo) {
                $erroresPorMesYCodigo[$codigo][$mes] = 0;
            }
        }

        foreach ($codResultAnual as $item) {
            $mes = \Carbon\Carbon::createFromFormat('Y-m', $item->mes)->format('M');
            $codigo = $item->codigo_resultado;
            $total = $item->total_error;

            $erroresPorMesYCodigo[$codigo][$mes] += $total;
        }

        $series = [];
        foreach ($erroresPorMesYCodigo as $codigo => $data) {
            $series[] = [
                'name' => $codigo,
                'data' => array_values($data) // Asegura orden por mes
            ];
        }

        //dd($apisMasErrores);
        $nombre_producto_errores = [];
        $errores_por_tipo = [
            'Error General' => [],
            'Internal Error' => [],
            'Error Producto' => [],
            '307' => [],
            '306' => [],
            '400' => [],
            'Internal E' => []
        ];

        foreach ($apisMasErrores as $dato) {
            $nombre_producto_errores[] = $dato->nombre_producto ?? '';
            // Usar valores por defecto 0 si la propiedad no existe
            $errores_por_tipo['307'][] = (int) ($dato->error_307 ?? 0);
            $errores_por_tipo['306'][] = (int) ($dato->error_306 ?? 0);
            $errores_por_tipo['Internal Error'][] = (int) ($dato->internal_error ?? 0);
            $errores_por_tipo['Internal E'][] = (int) ($dato->internal_e ?? 0);
            $errores_por_tipo['Error General'][] = (int) ($dato->error_general ?? 0);
            $errores_por_tipo['Error Producto'][] = (int) ($dato->error_producto ?? 0);
            $errores_por_tipo['400'][] = (int) ($dato->error_400 ?? 0);
        }


        $subtitulo = 'ÚLTIMOS 12 MESES';

        return view('dashboard-api/dashboard')->with(compact([
            'tiempoPromedioResp',
            'solicitudesTotales',
            'totalPromedioRespuestaGraf',
            'categorias',
            'aciertos',
            'errores',
            'totales',
            'total_aciertos',
            'total_errores',
            'totalConsultasProductGraf',
            'producto',
            'total_registros',
            'totalConsultasClienteGraf',
            'nombre_cliente',
            'consumo_total',
            'dataCodigoRespuesta',
            'tiempoPromedioAn',
            'mes_tiempoPromedio',
            'promedio_duracion',
            'cantidad_Respuesta',
            'codigo_resultado',
            'tiempo_api_resp',
            'nombre_api',
            'tiempoMayorMenor',
            'rangoTiempos',
            'cantidad_Tiempo',
            'rango_tiempo',
            'codResultAnual',
            'agrupado',
            'series',
            'nombre_producto_errores',
            'errores_por_tipo',
            'promedioConsumoApi',
            'promedioConsumoCliente',
            'mesConsultado',
            'clientesApis',
            'productosApis',
            'mesboton',
            'subtitulo',
            'tiempoProce'
        ]));
    }

    /*
        Esta funcion filtra los datos de las apis por los siguientes peroidos:
        - mensual
    */
    public static function filtroPorMes() {
        $mes = request::input('mes');

        $totalConsultasClienteGraf = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_CONSULTAS_CLIENTE_GRAF', NULL, $mes]);
        $totalConsultasProductGraf = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_CONSULTAS_PRODUCT_GRAF', NULL, $mes]);
        $tiempoPromedioResp = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TIEMPO_PROMEDIO', NULL, $mes]);
        $solicitudesTotales = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TOTAL_CONSULTAS', NULL, $mes]);
        $dataCodigoRespuesta = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_CODIGO_RESP_DATA', NULL, $mes]);
        $tiempoPromedioProduct = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TIEMPO_PRODUCT', NULL, $mes]);
        $tiempoMayorMenor = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TIEMPO_MAYOR_MENOR', NULL, $mes]);
        $rangoTiempos = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_RANGO_TIEMPO', NULL, $mes]);
        $apisMasErrores = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_APIS_MAS_ERRORES', NULL, $mes]);
        $promedioConsumoApi = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_PROMEDIO_CONSUM_PROD', NULL, $mes]);

        $promedioConsumoCliente = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_PROMEDIO_CONSUM_CLIENT', NULL, $mes]);
        // dd($tiempoPromedioResp);
        /**reestructuramos para totalConsultasProductGraf  */
        $producto = [];
        $total_registros = [];
        if (!empty($totalConsultasProductGraf)) {
            foreach ($totalConsultasProductGraf as $dato) {
                $producto[] = $dato->nombre_producto;
                $total_registros[] = $dato->total_registros;
            }
        }

        $mesConsultado = $tiempoPromedioResp[0]->mes_consultado ?? NULL;
        $tiempoPromedioResp = $tiempoPromedioResp[0]->segundos ?? 0;

        $solicitudesTotales = $solicitudesTotales[0];

        /**reestructuramos para dataCodigoRespuesta */
        $codigo_resultado = [];
        $cantidad_Respuesta = [];
        if (!empty($dataCodigoRespuesta)) {
            foreach ($dataCodigoRespuesta as $dato) {
                $codigo_resultado[] = $dato->codigo;
                $cantidad_Respuesta[] = $dato->cantidad;
            }
        }


        $tiempo_api_resp = [];
        $nombre_api = [];
        if (!empty($tiempoPromedioProduct)) {
            foreach ($tiempoPromedioProduct as $dato) {
                $tiempo_api_resp[] = $dato->promedio_duracion;
                $nombre_api[] = $dato->nombre_producto;
            }
        }
        /**reestructuramos para rangoTiempos */
        $rango_tiempo = [];
        $cantidad_Tiempo = [];
        if (!empty($rangoTiempos)) {
            foreach ($rangoTiempos as $dato) {
                $rango_tiempo[] = $dato->rango_tiempo;
                $cantidad_Tiempo[] = $dato->cantidad;
            }
        }

        $nombre_producto_errores = [];
        $errores_por_tipo = [
            'Error General' => [],
            'Internal Error' => [],
            'Error Producto' => [],
            '307' => [],
            '306' => [],
            '400' => [],
            'Internal E' => []
        ];
        if (!empty($apisMasErrores)) {
            foreach ($apisMasErrores as $dato) {
                $nombre_producto_errores[] = $dato->nombre_producto ?? '';
                // Usar valores por defecto 0 si la propiedad no existe
                $errores_por_tipo['307'][] = (int) ($dato->error_307 ?? 0);
                $errores_por_tipo['306'][] = (int) ($dato->error_306 ?? 0);
                $errores_por_tipo['Internal Error'][] = (int) ($dato->internal_error ?? 0);
                $errores_por_tipo['Internal E'][] = (int) ($dato->internal_e ?? 0);
                $errores_por_tipo['Error General'][] = (int) ($dato->error_general ?? 0);
                $errores_por_tipo['Error Producto'][] = (int) ($dato->error_producto ?? 0);
                $errores_por_tipo['400'][] = (int) ($dato->error_400 ?? 0);
            }
        }
        /**reestructuramos para totalConsultasClienteGraf */
        $nombre_cliente = [];
        $consumo_total = [];
        if (!empty($totalConsultasClienteGraf)) {
            foreach ($totalConsultasClienteGraf as $dato) {
                $nombre_cliente[] = $dato->nombre_cliente;
                $consumo_total[] = $dato->consumo_total;
            }
        }

        return response()->json([
            'productos' => $producto,
            'total_registros' => $total_registros,
            'totalConsultasProductGraf' => $totalConsultasProductGraf,
            'tiempoPromedioResp' => $tiempoPromedioResp,
            'solicitudesTotales' => $solicitudesTotales,
            'dataCodigoRespuesta' => $dataCodigoRespuesta,
            'cantidad_Respuesta' => $cantidad_Respuesta,
            'codigo_resultado' => $codigo_resultado,
            'tiempo_api_resp' => $tiempo_api_resp,
            'nombre_api' => $nombre_api,
            'tiempoMayorMenor' => $tiempoMayorMenor,
            'rangoTiempos' => $rangoTiempos,
            'cantidad_Tiempo' => $cantidad_Tiempo,
            'rango_tiempo' => $rango_tiempo,
            'nombre_producto_errores' => $nombre_producto_errores,
            'errores_por_tipo' => $errores_por_tipo,
            'promedioConsumoApi' => $promedioConsumoApi,
            'soymes' => $mesConsultado,
            'nombre_cliente' => $nombre_cliente,
            'consumo_total' => $consumo_total,
            'promedioConsumoCliente' => $promedioConsumoCliente
        ]);
    }

    /*
        Esta funcion filtra los datos de las apis por los siguientes peroidos:
        - diario
    */
    public static function filtroPeriodoApis() {
        $periodo = "diario";
        $mes = request::input('mes');

        $totalConsultasClienteGraf = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_CONSULTAS_CLIENTE_GRAF', $periodo]);
        $totalConsultasProductGraf = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_CONSULTAS_PRODUCT_GRAF', $periodo]);
        $tiempoPromedioResp = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TIEMPO_PROMEDIO', $periodo]);

        $solicitudesTotales = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TOTAL_CONSULTAS', $periodo]);

        $dataCodigoRespuesta = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_CODIGO_RESP_DATA', $periodo]);

        $tiempoPromedioProduct = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TIEMPO_PRODUCT', $periodo]);
        $tiempoMayorMenor = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TIEMPO_MAYOR_MENOR', $periodo]);

        $rangoTiempos = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_RANGO_TIEMPO', $periodo]);
        $apisMasErrores = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_APIS_MAS_ERRORES', $periodo]);
        $promedioConsumoApi = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_PROMEDIO_CONSUM_PROD', $periodo]);
        $promedioConsumoCliente = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_PROMEDIO_CONSUM_CLIENT', $periodo]);

        /**reestructuramos para totalConsultasProductGraf  */
        $producto = [];
        $total_registros = [];
        if (!empty($totalConsultasProductGraf)) {
            foreach ($totalConsultasProductGraf as $dato) {
                $producto[] = $dato->nombre_producto;
                $total_registros[] = $dato->total_registros;
            }
        }

        $solicitudesTotales = $solicitudesTotales[0];

        /**reestructuramos para dataCodigoRespuesta */
        $codigo_resultado = [];
        $cantidad_Respuesta = [];
        if (!empty($dataCodigoRespuesta)) {
            foreach ($dataCodigoRespuesta as $dato) {
                $codigo_resultado[] = $dato->codigo;
                $cantidad_Respuesta[] = $dato->cantidad;
            }
        }


        $tiempo_api_resp = [];
        $nombre_api = [];
        if (!empty($tiempoPromedioProduct)) {
            foreach ($tiempoPromedioProduct as $dato) {
                $tiempo_api_resp[] = $dato->promedio_duracion;
                $nombre_api[] = $dato->nombre_producto;
            }
        }
        /**reestructuramos para rangoTiempos */
        $rango_tiempo = [];
        $cantidad_Tiempo = [];
        if (!empty($rangoTiempos)) {
            foreach ($rangoTiempos as $dato) {
                $rango_tiempo[] = $dato->rango_tiempo;
                $cantidad_Tiempo[] = $dato->cantidad;
            }
        }

        $nombre_producto_errores = [];
        $errores_por_tipo = [
            'Error General' => [],
            'Internal Error' => [],
            'Error Producto' => [],
            '307' => [],
            '306' => [],
            '400' => [],
            'Internal E' => []
        ];
        if (!empty($apisMasErrores)) {
            foreach ($apisMasErrores as $dato) {
                $nombre_producto_errores[] = $dato->nombre_producto ?? '';
                // Usar valores por defecto 0 si la propiedad no existe
                $errores_por_tipo['307'][] = (int) ($dato->error_307 ?? 0);
                $errores_por_tipo['306'][] = (int) ($dato->error_306 ?? 0);
                $errores_por_tipo['Internal Error'][] = (int) ($dato->internal_error ?? 0);
                $errores_por_tipo['Internal E'][] = (int) ($dato->internal_e ?? 0);
                $errores_por_tipo['Error General'][] = (int) ($dato->error_general ?? 0);
                $errores_por_tipo['Error Producto'][] = (int) ($dato->error_producto ?? 0);
                $errores_por_tipo['400'][] = (int) ($dato->error_400 ?? 0);
            }
        }
        /**reestructuramos para totalConsultasClienteGraf */
        $nombre_cliente = [];
        $consumo_total = [];
        if (!empty($totalConsultasClienteGraf)) {
            foreach ($totalConsultasClienteGraf as $dato) {
                $nombre_cliente[] = $dato->nombre_cliente;
                $consumo_total[] = $dato->consumo_total;
            }
        }

        /**para colocar el dia de hoy en la vista */
        Carbon::setLocale('es'); //para que me de el mes en español
        $hoy = Carbon::now();

        $mesConsultado = $hoy->translatedFormat('j \d\e F');

        return response()->json([
            'productos' => $producto,
            'total_registros' => $total_registros,
            'totalConsultasProductGraf' => $totalConsultasProductGraf,
            'tiempoPromedioResp' => $tiempoPromedioResp,
            'solicitudesTotales' => $solicitudesTotales,
            'dataCodigoRespuesta' => $dataCodigoRespuesta,
            'cantidad_Respuesta' => $cantidad_Respuesta,
            'codigo_resultado' => $codigo_resultado,
            'tiempo_api_resp' => $tiempo_api_resp,
            'nombre_api' => $nombre_api,
            'tiempoMayorMenor' => $tiempoMayorMenor,
            'rangoTiempos' => $rangoTiempos,
            'cantidad_Tiempo' => $cantidad_Tiempo,
            'rango_tiempo' => $rango_tiempo,
            'nombre_producto_errores' => $nombre_producto_errores,
            'errores_por_tipo' => $errores_por_tipo,
            'promedioConsumoApi' => $promedioConsumoApi,
            'soymes' => $mesConsultado,
            'nombre_cliente' => $nombre_cliente,
            'consumo_total' => $consumo_total,
            'promedioConsumoCliente' => $promedioConsumoCliente

        ]);
    }




    public static function filtradoDatosApis() {
        Carbon::setLocale('es'); //para que me de el mes en español    

        $periodo = Request::input('periodo') ?? NULL;
        $mes = Request::input('mes') ?? NULL;
        $productoApi = Request::input('producto') ?? NULL;
        $cliente = Request::input('cliente') ?? NULL;
        $dia = Request::input('dia') ?? NULL;
        // dd($mes);

        if ($periodo == 'periodo_mensual') {
            $periodo = 'mensual';
            $mes = NULL;
        } elseif ($periodo == 'periodo_diario') {
            $periodo = 'diario';
        } elseif ($periodo == 'periodo_actual') {
            $periodo = 'actual';
        }



        $solicitudesTotales = SQLConnection::backendStoreProcedure('getTotalConsultasApi', [$periodo, $mes, $productoApi, $cliente]);
        $solicitudesTotales = $solicitudesTotales[0];


        $tiempoPromedioResp = SQLConnection::backendStoreProcedure('getTiempoPromedioApi', [$periodo, $mes, $productoApi, $cliente]);
        $tiempoPromedioResp = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TIEMPO_PROMEDIO', $periodo, $mes, $productoApi, $cliente]);
        $tiempoMayorMenor = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TIEMPO_MAYOR_MENOR', $periodo, $mes, $productoApi, $cliente]);
        $promedioConsumoApi = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_PROMEDIO_CONSUM_PROD', $periodo, $mes, $productoApi, $cliente]);
        $promedioConsumoCliente = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_PROMEDIO_CONSUM_CLIENT', $periodo, $mes, $productoApi, $cliente]);

        $totalConsultasClienteGraf = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_CONSULTAS_CLIENTE_GRAF', $periodo, $mes, $productoApi, $cliente]);

        // dd($cliente);
        $tiempoProce = SQLConnection::backendStoreProcedure('api_promedioTiempoProcesamiento', [$periodo, $mes, $productoApi, $cliente]);
        //dd($mes);
        //$tiempoProce =  SQLConnection::backendStoreProcedure('api_promedioTiempoProcesamiento',['mensual',3, 92, 4405537]);  
        //dd($tiempoProce);
        $nombre_cliente = [];
        $consumo_total = [];
        if (!empty($totalConsultasClienteGraf)) {
            foreach ($totalConsultasClienteGraf as $dato) {
                $nombre_cliente[] = $dato->nombre_cliente;
                $consumo_total[] = $dato->consumo_total;
            }
        }

        $totalConsultasProductGraf = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_CONSULTAS_PRODUCT_GRAF', $periodo, $mes, $productoApi, $cliente]);
        $producto = [];
        $total_registros = [];
        if (!empty($totalConsultasProductGraf)) {
            foreach ($totalConsultasProductGraf as $dato) {
                $producto[] = $dato->nombre_producto;
                $total_registros[] = $dato->total_registros;
            }
        }

        $dataCodigoRespuesta = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_CODIGO_RESP_DATA', $periodo, $mes, $productoApi, $cliente]);
        $codigo_resultado = [];
        $cantidad_Respuesta = [];
        if (!empty($dataCodigoRespuesta)) {
            foreach ($dataCodigoRespuesta as $dato) {
                $codigo_resultado[] = $dato->codigo;
                $cantidad_Respuesta[] = $dato->cantidad;
            }
        }

        $tiempoPromedioProduct = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TIEMPO_PRODUCT', $periodo, $mes, $productoApi, $cliente]);
        $tiempo_api_resp = [];
        $nombre_api = [];
        if (!empty($tiempoPromedioProduct)) {
            foreach ($tiempoPromedioProduct as $dato) {
                $tiempo_api_resp[] = $dato->promedio_duracion;
                $nombre_api[] = $dato->nombre_producto;
            }
        }


        $rangoTiempos = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_RANGO_TIEMPO', $periodo, $mes, $productoApi, $cliente]);
        $rango_tiempo = [];
        $cantidad_Tiempo = [];
        if (!empty($rangoTiempos)) {
            foreach ($rangoTiempos as $dato) {
                $rango_tiempo[] = $dato->rango_tiempo;
                $cantidad_Tiempo[] = $dato->cantidad;
            }
        }

        $apisMasErrores = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_APIS_MAS_ERRORES', $periodo, $mes, $productoApi, $cliente]);
        $nombre_producto_errores = [];
        $errores_por_tipo = [
            'Error General' => [],
            'Internal Error' => [],
            'Error Producto' => [],
            '307' => [],
            '306' => [],
            '400' => [],
            'Internal E' => []
        ];
        if (!empty($apisMasErrores)) {
            foreach ($apisMasErrores as $dato) {
                $nombre_producto_errores[] = $dato->nombre_producto ?? '';
                // Usar valores por defecto 0 si la propiedad no existe
                $errores_por_tipo['307'][] = (int) ($dato->error_307 ?? 0);
                $errores_por_tipo['306'][] = (int) ($dato->error_306 ?? 0);
                $errores_por_tipo['Internal Error'][] = (int) ($dato->internal_error ?? 0);
                $errores_por_tipo['Internal E'][] = (int) ($dato->internal_e ?? 0);
                $errores_por_tipo['Error General'][] = (int) ($dato->error_general ?? 0);
                $errores_por_tipo['Error Producto'][] = (int) ($dato->error_producto ?? 0);
                $errores_por_tipo['400'][] = (int) ($dato->error_400 ?? 0);
            }
        }


        $totalPromedioRespuestaGraf = SQLConnection::backendStoreProcedure('BackendDashboardApi', ['GET_TOTAL_CONSULTAS_GRAF', $periodo, $mes, $productoApi, $cliente]);
        // dd($totalPromedioRespuestaGraf);
        /** reestructuramos el total para meses, errores, ok, y totales */
        $mesNombre = [];
        $categorias = [];
        $aciertos = [];
        $errores = [];
        $totales = [];
        $total_aciertos = 0;
        $total_errores = 0;

        //  dd($totalPromedioRespuestaGraf);
        if ($periodo == 'mensual') {
            foreach ($totalPromedioRespuestaGraf as $dato) {
                $mesNombre = \Carbon\Carbon::createFromFormat('Y-m', $dato->mes)->translatedFormat('M'); // Mes abreviado (Ene, Feb, etc.)
                $categorias[] = $mesNombre;
                $aciertos[] = $dato->total_ok;
                $errores[] = $dato->total_error;
                $totales[] = $dato->total_consumos;
            }
        } elseif ($periodo == 'diario') {
            foreach ($totalPromedioRespuestaGraf as $dato) {
                $fecha = Carbon::createFromFormat('Y-m-d', $dato->mes);
                // Sacar el nombre del mes abreviado y el día
                $mesNombre = $fecha->translatedFormat('D d/m'); // Ej: "Abr 27"
                $categorias[] = $mesNombre;
                $aciertos[] = $dato->total_ok;
                $errores[] = $dato->total_error;
                $totales[] = $dato->total_consumos;
            }
        }

        $codResultAnual = SQLConnection::backendStoreProcedure('api_TasaErrores', [$periodo, $mes, $productoApi, $cliente]);

        $campoFecha = $periodo == 'diario' ? 'dia' : 'mes';

        // Agrupar por fechas formateadas
        $agrupado = collect($codResultAnual)
            ->pluck($campoFecha)
            ->map(function ($fecha) use ($periodo) {
                return $periodo == 'diario'
                    ? Carbon::createFromFormat('Y-m-d', $fecha)->translatedFormat('D d/m') // Ej: "22 Abr"
                    : Carbon::createFromFormat('Y-m', $fecha)->translatedFormat('M');     // Ej: "Abr"
            })
            ->unique()
            ->values()
            ->all(); // <- convierte a array simple para JS

        // dd($agrupado); // Revisa las claves generadas

        // Obtener códigos únicos
        $codigos = collect($codResultAnual)
            ->pluck('codigo_resultado')
            ->unique()
            ->values()
            ->all();

        // Inicializar estructura de errores
        $erroresPorPeriodoYCodigo = [];

        foreach ($codigos as $codigo) {
            foreach ($agrupado as $fecha) {
                $erroresPorPeriodoYCodigo[$codigo][$fecha] = 0;
            }
        }

        // Sumar totales por código y fecha
        foreach ($codResultAnual as $item) {
            $fechaFormateada = $periodo == 'diario'
                ? Carbon::createFromFormat('Y-m-d', $item->dia)->translatedFormat('D d/m')
                : Carbon::createFromFormat('Y-m', $item->mes)->translatedFormat('M');

            // Debugging: Verifica el valor de fecha
            // dd($fechaFormateada); // Verifica la fecha formateada

            $codigo = $item->codigo_resultado;
            $total = $item->total_error;

            // Asegura que la fecha existe en los errores por periodo
            if (isset($erroresPorPeriodoYCodigo[$codigo][$fechaFormateada])) {
                $erroresPorPeriodoYCodigo[$codigo][$fechaFormateada] += $total;
            }
        }

        // Preparar series para Highcharts
        $series = [];

        foreach ($erroresPorPeriodoYCodigo as $codigo => $data) {
            $series[] = [
                'name' => $codigo,
                'data' => array_values($data), // Asegura el orden según agrupado
            ];
        }

        // Retornar la respuesta para JS
        return response()->json([
            'agrupado' => $agrupado, // etiquetas de eje X
            'series' => $series,     // datos agrupados por código
        ]);

    }



    function procesarEstadisticasConsumo() {
        //$fecha_hoy = date('Y-m-d');
        $fecha_hoy = date('Y-m-d', strtotime('-1 day'));
        // $db = DB::connection('dbtest');

        // $detalle = $db->select('EXEC enriqEstadisticaConsumoDetalle ?', [$fecha_hoy]);
        // $resumen = $db->select('EXEC enriqEstadisticaConsumo ?', [$fecha_hoy]);


        $detalle = SQLConnection::backendStoreProcedure('enriqEstadisticaConsumoDetalle', [$fecha_hoy]);
        $resumen = SQLConnection::backendStoreProcedure('enriqEstadisticaConsumo', [$fecha_hoy]);

        return json_encode([
            'fecha' => $fecha_hoy,   // ← esto es lo que necesitás para ver la fecha desde curl
            'detalle' => $detalle,
            'resumen' => $resumen
        ]);
    }


    /*public static function cargarVistaClientesApis() {
        $clientes = Request::Input("cliente") ?? null;
        $resultado = SQLConnection::backendStoreProcedure('panel_clientes_api_usuarios', array($clientes));
        //dd($resultado);
        // $documentosAgrupados = collect($resultado)->groupBy('nombre_cliente');
        $documentosAgrupados = [];

        foreach ($resultado as $doc) {
            // Asegurar nombre de cliente
            $cliente = $doc->nombre_cliente ?: 'Sin Nombre Cliente';

            // Agrupar por cliente
            if (!isset($documentosAgrupados[$cliente])) {
                $documentosAgrupados[$cliente] = [];
            }
            $documentosAgrupados[$cliente][] = $doc;
            $mesAnterior = (int) ($doc->mes_anterior ?? 0);
            $mesActual = (int) ($doc->mes_actual ?? 0);
            $precioUnitario = (float) ($doc->precio_unitario ?? 0);
            $tieneEscalonado = (int) ($doc->tiene_escalonado ?? 0);
            $minimoFacturable = (float) ($doc->minimo_facturable ?? 0);
            $valorFacturable = 0;
            $valorEstimadoMesActual = 0;
            $diasDelMes = Carbon::now()->daysInMonth;
            $diaActual = Carbon::now()->day - 1; // Día actual menos 1 para excluir el día en curso
	    $diaActual = $diaActual == 0 ? 1 : $diaActual;
            // SIN ESCALONADO
            if ($tieneEscalonado === 0) {
                $valorFacturable = $mesAnterior * $precioUnitario;
                // Calculo valor estimado mes actual
                $valorEstimadoMesActual = $mesActual * $precioUnitario / $diaActual * $diasDelMes;
            }

            // CON ESCALONADO
            if ($tieneEscalonado === 1 && !empty($doc->precios_escalonados)) {

                $escalones = json_decode($doc->precios_escalonados, true);
                $tipoEscalonado = (int) ($doc->tipo_escalonado ?? 1);

                if (is_array($escalones)) {

                    if ($tipoEscalonado === 2) {
                        // POR LICENCIA: cálculo acumulativo (A + B + C)
                        $consumidoHastaAhora = 0;
                        foreach ($escalones as $escalon) {
                            if ($mesAnterior <= $consumidoHastaAhora) break;
                            $hasta = $escalon['hasta'] ?? null;
                            $precioEscalon = (float) $escalon['precio'];
                            $esInfinito = ($hasta === null || $hasta === '');
                            $topeTramo = $esInfinito ? $mesAnterior : (int) $hasta;
                            $unidades = min($mesAnterior, $topeTramo) - $consumidoHastaAhora;
                            if ($unidades > 0) {
                                $valorFacturable += $unidades * $precioEscalon;
                                $consumidoHastaAhora = min($mesAnterior, $topeTramo);
                            }
                        }
                        // Estimado mes actual (por licencia)
                        $consumidoHastaAhora = 0;
                        foreach ($escalones as $escalon) {
                            if ($mesActual <= $consumidoHastaAhora) break;
                            $hasta = $escalon['hasta'] ?? null;
                            $precioEscalon = (float) $escalon['precio'];
                            $esInfinito = ($hasta === null || $hasta === '');
                            $topeTramo = $esInfinito ? $mesActual : (int) $hasta;
                            $unidades = min($mesActual, $topeTramo) - $consumidoHastaAhora;
                            if ($unidades > 0) {
                                $valorEstimadoMesActual += ($unidades / $diaActual) * $diasDelMes * $precioEscalon;
                                $consumidoHastaAhora = min($mesActual, $topeTramo);
                            }
                        }
                    } else {
                        // ESCALONADO CLÁSICO: p × q total según el escalón en que cae
                        foreach ($escalones as $escalon) {
                            $desde = (int) $escalon['desde'];
                            $hasta = $escalon['hasta'] ?? null;
                            $precioEscalon = (float) $escalon['precio'];
                            $esInfinito = ($hasta === null || $hasta === '');
                            if ($mesAnterior >= $desde && ($esInfinito || $mesAnterior <= $hasta)) {
                                $valorFacturable = $mesAnterior * $precioEscalon;
                                $doc->precio_escalon_facturable = $precioEscalon;
                                break;
                            }
                        }
                        // Estimado mes actual (clásico)
                        foreach ($escalones as $escalon) {
                            $desde = (int) $escalon['desde'];
                            $hasta = $escalon['hasta'] ?? null;
                            $precioEscalon = (float) $escalon['precio'];
                            $esInfinito = ($hasta === null || $hasta === '');
                            if ($mesActual >= $desde && ($esInfinito || $mesActual <= $hasta)) {
                                $valorEstimadoMesActual = ($mesActual * $precioEscalon / $diaActual) * $diasDelMes;
                                break;
                            }
                        }
                    }
                }
            }

            // Guardarlo en el objeto
            $doc->valor_facturable = round($valorFacturable, 2);
            $doc->precio_escalon_estimado = round($valorEstimadoMesActual, 2);
            // Evaluo que el valor calculado sea mayor al minimo facturable sino tomo el minimo
            if ($valorFacturable < $minimoFacturable) {
                $doc->valor_facturable = $minimoFacturable;
            }
        }
        //dd($documentosAgrupados);
        return view('dashboard-api/dashboard_clientes_new')->with(compact(['documentosAgrupados']));
    }*/

    public static function cargarVistaClientesApis() {
        $clientes = Request::Input("cliente") ?? null;
        $resultado = SQLConnection::backendStoreProcedure('panel_clientes_api_usuarios', array('vista', $clientes));
        
        $documentosAgrupados = [];

        foreach ($resultado as $doc) {
            $cliente = $doc->nombre_cliente ?: 'Sin Nombre Cliente';

            // Agrupamos los documentos por el nombre del cliente
            if (!isset($documentosAgrupados[$cliente])) {
                $documentosAgrupados[$cliente] = [];
            }
            
            $documentosAgrupados[$cliente][] = $doc;
            
        }

        return view('dashboard-api/dashboard_clientes_new')->with(compact(['documentosAgrupados']));
    }

    public static function descargarExcelFiltro() {
        $cliente = Request::Input("cliente");
        $fecha_desde = Request::Input("fecha_desde");
        $fecha_hasta = Request::Input("fecha_hasta");
        //dd($cliente);
        $resultado = SQLConnection::backendStoreProcedure('panel_clientes_api', array($cliente, $fecha_desde, $fecha_hasta));
        return $resultado;
    }

    public static function validarFecha($fecha, $formato = 'Y-m-d') {
        $d = DateTime::createFromFormat($formato, $fecha);
        return $d && $d->format($formato) === $fecha;  
    }
    
    public function descargarExcel(Request $request) {
	    ini_set('max_execution_time',300);
        $cliente = Request::Input("cliente");
        $nombreCliente = Request::Input("nombrecliente");
        $productos = Request::Input('apis', []);
        $nombres_productos = Request::Input('nombres_apis', []);
        $fechaDesde = Request::Input('fecha_desde');
        $fechaHasta = Request::Input('fecha_hasta');
        $fechaDesdeArchivo = Carbon::parse($fechaDesde)->format('Ymd');
        $fechaHastaArchivo = Carbon::parse($fechaHasta)->format('Ymd');
   
        // Validación fechas
        $fechaDesdeValida = self::validarFecha($fechaDesde);
        $fechaHastaValida = self::validarFecha($fechaHasta);

        if (!$fechaDesdeValida && !$fechaHastaValida) {
            $fechaDesde = Carbon::now()->subMonth()->startOfMonth()->format('Y-m-d');
            $fechaHasta = Carbon::now()->subMonth()->endOfMonth()->format('Y-m-d');
        } elseif (!$fechaDesdeValida || !$fechaHastaValida) {
            return back()->with('error', 'Fechas inválidas.');
        } elseif (Carbon::parse($fechaHasta)->lt(Carbon::parse($fechaDesde))) {
            $fechaDesde = Carbon::now()->subMonth()->startOfMonth()->format('Y-m-d');
            $fechaHasta = Carbon::now()->subMonth()->endOfMonth()->format('Y-m-d');
        } else {
            $fechaDesde = Carbon::parse($fechaDesde)->startOfDay()->format('Y-m-d');
            $fechaHasta = Carbon::parse($fechaHasta)->endOfDay()->format('Y-m-d');
        }
   
        if (!$cliente || empty($productos)) {
            return back()->with('error', 'Faltan datos.');
        }
   
        // Rutas
        $nombreClienteArchivo = preg_replace('/[^A-Za-z0-9]/', '', $nombreCliente ?? '');
        $fechaDesdeArchivo = Carbon::parse($fechaDesde)->format('Ymd');
        $fechaHastaArchivo = Carbon::parse($fechaHasta)->format('Ymd');
        $fileName = 'Reporte_Api_' . $nombreClienteArchivo . '_' . $fechaDesdeArchivo . '_' . $fechaHastaArchivo . '.xml';
        $dir = env('PATH_EXCEL_API');
   
        if (!is_dir($dir)) mkdir($dir, 0777, true);
   
        $filePath = $dir . '/' . $fileName;
        $fh = fopen($filePath, 'w');
        // Cabecera XML
        fwrite($fh, <<<XML
        <?xml version="1.0"?>
        <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
        xmlns:o="urn:schemas-microsoft-com:office:office"
        xmlns:x="urn:schemas-microsoft-com:office:excel"
        xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
        xmlns:html="http://www.w3.org/TR/REC-html40">
        XML);
        //fwrite libera memoria despues de cada escritura
        foreach ($productos as $index => $productoId) {
            $nombre = $nombres_productos[$index] ?? $productoId;
            $titulo = $nombre;
            fwrite($fh, "\n  <Worksheet ss:Name=\"{$titulo}\">\n    <Table>\n");
            try {
                $datos = SQLConnection::backendStoreProcedure('Reporte_consumos_api_', [
                    'rptagrupado',
                    $cliente,
                    $productoId,
                    $fechaDesde,
                    $fechaHasta,
                    null,
                ]);
            } catch (\Exception $e) {
                // Handle the error appropriately
                $datos = []; // Set to empty array or handle as needed
                Log::error('Error fetching data from stored procedure: ' . $e->getMessage());
            }
            if (!empty($datos)) {
                $cols = array_keys((array)$datos[0]);
   
                // Encabezado
                fwrite($fh, "      <Row>\n");
                foreach ($cols as $col) {
                    $colEsc = htmlspecialchars($col, ENT_QUOTES);
                    fwrite($fh, "        <Cell><Data ss:Type=\"String\">{$colEsc}</Data></Cell>\n");
                }
                fwrite($fh, "      </Row>\n");
   
                // Filas
                foreach ($datos as $fila) {
                    $fila = (array)$fila;
                    fwrite($fh, "      <Row>\n");
                    foreach ($cols as $col) {
                        $valor = $fila[$col] ?? '';
                        $tipo = is_numeric($valor) ? 'Number' : 'String';
                        $valEsc = htmlspecialchars($valor, ENT_QUOTES);
                        fwrite($fh, "        <Cell><Data ss:Type=\"{$tipo}\">{$valEsc}</Data></Cell>\n");
                    }
                    fwrite($fh, "      </Row>\n");
                }
            } else {
                fwrite($fh, "      <Row><Cell><Data ss:Type=\"String\">No hay datos</Data></Cell></Row>\n");
            }
   
            fwrite($fh, "    </Table>\n  </Worksheet>\n");
            //destruye la variable despues usarla
            unset($datos);
        }
   
        fwrite($fh, "</Workbook>");
        fclose($fh);
   
        // Forzar descarga
        $publicPath= env('PATH_EXCEL_API') . '/' . $fileName;
        return response()->download($publicPath, $fileName, [
            'Content-Type' => 'application/vnd.ms-excel',
            'Content-Disposition' => 'attachment; filename="' . str_replace('.xml', '.xls', $fileName) . '"'
        ])->deleteFileAfterSend(true);
   
    }

    public function descargarExcelFacturacion(Request $request) {
        $fecha_desde = Request::input('fecha_desde_fact');
        $fecha_hasta = Request::input('fecha_hasta_fact');

        $fechaDesde = Carbon::parse($fecha_desde)->startOfDay()->format('Y-m-d');
        $fechaHasta = Carbon::parse($fecha_hasta)->endOfDay()->format('Y-m-d');

        // Ejecutamos el SP (La matemática ya viene resuelta de SQL)
        $dataRaw = SQLConnection::backendStoreProcedure('panel_clientes_api_usuarios', ['Excel', null, $fechaDesde, $fechaHasta]);

        $data = [];

        foreach ($dataRaw as $doc) {
            $fila = (array) $doc;

            // Saltamos filas sin precio ni escalones configurados (Igual que el continue original)
//            if (($fila['precio_unitario'] ?? 0) == 0 && ($fila['tiene_escalonado'] ?? 0) !== 1) {
if (($fila['precio_unitario'] ?? 0) == 0 && ($fila['tiene_escalonado'] ?? 0) !== 1 && ($fila['consumo_especifico'] ?? 0) == 0) {
                continue;
            }




            // Filtramos para asegurar que solo van al Excel si hay consumo o hay algo que facturar
            if (($fila['consumo_especifico'] ?? 0) > 0 || ($fila['valor_facturable'] ?? 0) > 0) {
                
                // Adaptamos las keys para que encajen exacto con las columnas que tu código XML de abajo requiere
                $fila['cliente_tango']  = $fila['codigo_cliente'] ?? '';
                $fila['minimo_factura'] = $fila['minimo_facturable'] ?? 0;
                $fila['tango_producto'] = $fila['tango_code'] ?? '';
                $fila['desde']          = $fecha_desde;
                $fila['hasta']          = $fecha_hasta;

                $data[] = $fila;
            }
        }

        // --- Generar Excel ---
        $fileName = 'Facturacion.xml';
        $dir = env('PATH_EXCEL_API');
        if (!is_dir($dir)) mkdir($dir, 0777, true);
        $filePath = $dir . '/' . $fileName;
        $fh = fopen($filePath, 'w');

        fwrite($fh, <<<XML
        <?xml version="1.0"?>
        <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
        xmlns:o="urn:schemas-microsoft-com:office:office"
        xmlns:x="urn:schemas-microsoft-com:office:excel"
        xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
        xmlns:html="http://www.w3.org/TR/REC-html40">
        XML);

        fwrite($fh, "\n  <Worksheet ss:Name=\"Facturacion\">\n    <Table>\n");

        if (!empty($data)) {
            $cols = [
                'cliente', 'nombre_cliente', 'nombre_producto', 'cliente_tango',
                'precio_unitario', 'moneda', 'minimo_factura', 'tango_producto',
                'consumo_especifico', 'valor_facturable', 'desde', 'hasta', 'id_producto',
            ];
            fwrite($fh, "      <Row>\n");
            foreach ($cols as $col) {
                $header = htmlspecialchars($col, ENT_QUOTES);
                fwrite($fh, "        <Cell><Data ss:Type=\"String\">{$header}</Data></Cell>\n");
            }
            fwrite($fh, "      </Row>\n");
            foreach ($data as $fila) {
                fwrite($fh, "      <Row>\n");
                foreach ($cols as $col) {
                    $valor = $fila[$col] ?? '';
                    $tipo = is_numeric($valor) ? 'Number' : 'String';
                    $valEsc = htmlspecialchars($valor, ENT_QUOTES);
                    fwrite($fh, "        <Cell><Data ss:Type=\"{$tipo}\">{$valEsc}</Data></Cell>\n");
                }
                fwrite($fh, "      </Row>\n");
            }
        } else {
            fwrite($fh, "      <Row><Cell><Data ss:Type=\"String\">No hay datos</Data></Cell></Row>\n");
        }

        fwrite($fh, "    </Table>\n  </Worksheet>\n");
        fwrite($fh, "</Workbook>");
        fclose($fh);

        return response()->download($filePath, str_replace('.xml', '.xls', $fileName), [
            'Content-Type' => 'application/vnd.ms-excel',
            'Content-Disposition' => 'attachment; filename="' . str_replace('.xml', '.xls', $fileName) . '"'
        ])->deleteFileAfterSend(true);
    }

    /*public function descargarExcelFacturacion(Request $request)
    {
        $fecha_desde = Request::input('fecha_desde_fact');
        $fecha_hasta = Request::input('fecha_hasta_fact');

        $fechaDesde = Carbon::parse($fecha_desde)->startOfDay()->format('Y-m-d');
        $fechaHasta = Carbon::parse($fecha_hasta)->endOfDay()->format('Y-m-d');

        // Ejecutar SP
        $data = SQLConnection::backendStoreProcedure('panel_clientes_api_facturar', [null, $fechaDesde, $fechaHasta]);

        // Recalcular valor_facturable para todos los contratos (igual que la vista)
        $cacheContrato = [];
        $cacheEscalones = [];
        foreach ($data as $fila) {
            $idCliente  = $fila->cliente;
            $idProducto = $fila->id_producto;
            $cacheKey   = $idCliente . '_' . $idProducto;

            if (!isset($cacheContrato[$cacheKey])) {
                $cacheContrato[$cacheKey] = SQLConnection::backendStoreProcedure('detalle_cliente_apis', ['obtener_consumos', $idCliente, $idProducto], 'selectOne');
            }
            $contrato = $cacheContrato[$cacheKey];

            $tipoEscalonado = (int) ($contrato->tipo_escalonado ?? 0);
            $consumo        = (int) ($fila->consumo ?? 0);
            $minimoFactura  = (float) ($fila->minimo_factura ?? 0);
            $valorFacturable = 0;

            if ($tipoEscalonado === 0) {
                // Sin escalonado: consumo × precio unitario
                $precioUnitario  = (float) ($fila->precio_unitario ?? 0);
                $valorFacturable = $consumo * $precioUnitario;
            } else {
                if (!isset($cacheEscalones[$cacheKey])) {
                    $cacheEscalones[$cacheKey] = SQLConnection::backendStoreProcedure('precios_escalonados_', ['S', null, $idCliente, $idProducto], 'select');
                }
                $escalones = $cacheEscalones[$cacheKey];

                if ($tipoEscalonado === 2) {
                    // Por licencia: cálculo acumulativo (A + B + C)
                    $consumidoHastaAhora = 0;
                    foreach ($escalones as $escalon) {
                        if ($consumo <= $consumidoHastaAhora) break;
                        $hasta         = $escalon->hasta ?? null;
                        $precioEscalon = (float) $escalon->precio;
                        $esInfinito    = ($hasta === null || $hasta === '');
                        $topeTramo     = $esInfinito ? $consumo : (int) $hasta;
                        $unidades      = min($consumo, $topeTramo) - $consumidoHastaAhora;
                        if ($unidades > 0) {
                            $valorFacturable    += $unidades * $precioEscalon;
                            $consumidoHastaAhora = min($consumo, $topeTramo);
                        }
                    }
                } else {
                    // Escalonado clásico: p × q según el escalón en que cae
                    foreach ($escalones as $escalon) {
                        $desde         = (int) $escalon->desde;
                        $hasta         = $escalon->hasta ?? null;
                        $precioEscalon = (float) $escalon->precio;
                        $esInfinito    = ($hasta === null || $hasta === '');
                        if ($consumo >= $desde && ($esInfinito || $consumo <= (int) $hasta)) {
                            $valorFacturable = $consumo * $precioEscalon;
                            break;
                        }
                    }
                }
            }

            $fila->valor_facturable = round(max($valorFacturable, $minimoFactura), 2);
        }

        // Definir nombre archivo
        $fileName = 'Facturacion.xml';
        $dir = env('PATH_EXCEL_API');

        if (!is_dir($dir)) mkdir($dir, 0777, true);

        $filePath = $dir . '/' . $fileName;
        $fh = fopen($filePath, 'w');

        // === Cabecera XML Excel ===
        fwrite($fh, <<<XML
        <?xml version="1.0"?>
        <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
        xmlns:o="urn:schemas-microsoft-com:office:office"
        xmlns:x="urn:schemas-microsoft-com:office:excel"
        xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
        xmlns:html="http://www.w3.org/TR/REC-html40">
        XML);

        // === Comenzamos hoja única ===
        fwrite($fh, "\n  <Worksheet ss:Name=\"Facturacion\">\n    <Table>\n");

        if (!empty($data)) {
            // Tomar columnas exactamente como vienen del SP
            $firstRow = (array)$data[0];
            $cols = array_keys($firstRow);

            // Encabezado
            fwrite($fh, "      <Row>\n");
            foreach ($cols as $col) {
                $header = htmlspecialchars($col, ENT_QUOTES);
                fwrite($fh, "        <Cell><Data ss:Type=\"String\">{$header}</Data></Cell>\n");
            }
            fwrite($fh, "      </Row>\n");

            // Filas de datos
            foreach ($data as $fila) {
                $fila = (array)$fila;

                fwrite($fh, "      <Row>\n");
                foreach ($cols as $col) {
                    $valor = $fila[$col] ?? '';
                    $tipo = is_numeric($valor) ? 'Number' : 'String';
                    $valEsc = htmlspecialchars($valor, ENT_QUOTES);
                    fwrite($fh, "        <Cell><Data ss:Type=\"{$tipo}\">{$valEsc}</Data></Cell>\n");
                }
                fwrite($fh, "      </Row>\n");
            }
        } else {
            fwrite($fh, "      <Row><Cell><Data ss:Type=\"String\">No hay datos</Data></Cell></Row>\n");
        }

        fwrite($fh, "    </Table>\n  </Worksheet>\n");
        fwrite($fh, "</Workbook>");
        fclose($fh);

        // === Descargar como .xls (aunque el contenido sea XML Excel-compatible) ===
        $publicPath = $filePath;
        return response()->download($publicPath, str_replace('.xml', '.xls', $fileName), [
            'Content-Type' => 'application/vnd.ms-excel',
            'Content-Disposition' => 'attachment; filename="' . str_replace('.xml', '.xls', $fileName) . '"'
        ])->deleteFileAfterSend(true);
    }*/

    public static function obtenerDetalleClienteApi() {
        $cliente = Request::Input("cliente");
        $nombre_cliente = Request::Input("nombreCliente");
        $producto = Request::Input("producto");
        $id_producto = Request::Input("id_producto");
        $data_consumo = SQLConnection::backendStoreProcedure('detalle_cliente_apis', array('obtener_consumos',$cliente, $id_producto), 'selectOne');
        $data_consumo->fecha_desde= Carbon::parse($data_consumo->fecha_desde)->format('d/m/Y');
        $data_consumo->fecha_hasta= Carbon::parse($data_consumo->fecha_hasta)->format('d/m/Y');
        $usuarios = SQLConnection::backendStoreProcedure('detalle_cliente_apis', array('usuarios_apis',$cliente, $id_producto), 'select');
        foreach($usuarios as $usuario){
            $usuario->vigencia = Carbon::parse($usuario->vigencia)->format('d/m/Y');
        }
        $escalones = SQLConnection::backendStoreProcedure('precios_escalonados_', array('S',null, $cliente, $id_producto), 'select');
        return view('dashboard-api/detalle_clientes_api')->with(compact(['cliente', 'nombre_cliente', 'producto', 'id_producto', 'data_consumo', 'usuarios', 'escalones']));
    }

    public static function obtenerDetalleUsuarioApi() {
        $user_id = Request::Input("user_id");
        $id_cliente = Request::Input("id_cliente");
        $id_producto = Request::Input("id_producto");
        $nombre_cliente = Request::Input("nombre_cliente");
        $nombre_producto = Request::Input("nombre_producto");
        $usuario = SQLConnection::backendStoreProcedure('detalle_cliente_apis', array('detalle_usuario_api',$id_cliente, $id_producto, $user_id), 'selectOne');
        $usuario->vigencia = Carbon::parse($usuario->vigencia)->format('Y-m-d');
        return view('dashboard-api/modal_detalle_usuario')->with(compact(['user_id', 'usuario', 'nombre_cliente', 'nombre_producto']));
    }

    public static function deshabilitarApiKey() {
        $id_api_key = Request::Input("id_api_key");
        try {
            $resultado = SQLConnection::backendStoreProcedure('detalle_cliente_apis', array('deshabilitar_api_key', null, null, null, $id_api_key));
            return response()->json(['success' => true, 'message' => 'API Key deshabilitada correctamente.']);
        } catch (Throwable $e) {
            return response()->json(['success' => false, 'message' => 'Error al deshabilitar la API Key: ']);
        }    
    }

    public static function habilitarApiKey() {
        $id_api_key = Request::Input("id_api_key");
        try {
            $resultado = SQLConnection::backendStoreProcedure('detalle_cliente_apis', array('habilitar_api_key', null, null, null, $id_api_key));
            return response()->json(['success' => true, 'message' => 'API Key habilitada correctamente.']);
        } catch (Throwable $e) {
            return response()->json(['success' => false, 'message' => 'Error al habilitar la API Key: ' . $e->getMessage()]);
        }
    }

    public static function regenerarApiKey() {
        $id_api_key = Request::Input("id_api_key");
        $valores = '0123456789QWERTYUIOPASDFGHJKLZXCVBNM';
        $nuevo_token =  substr(str_shuffle($valores),0,20);
        $user_id = Session::get('usuario')->user_id;
        try {
            $resultado = SQLConnection::backendStoreProcedure('apikey_cambio_apikey',array($id_api_key,$nuevo_token,$user_id));
            return response()->json(['success' => true, 'message' => 'API Key habilitada correctamente.', 'nuevo_token' => $nuevo_token]);
        } catch (Throwable $e) {
            return response()->json(['success' => false, 'message' => 'Error al habilitar la API Key: ' . $e->getMessage()]);
        }
    }

    public static function actualizarUsuarioApi() {
        $user_id = Request::Input("user_id");
        $nombre = Request::Input("first_name");
        $apellido = Request::Input("last_name");
        $password = Request::Input("password");
        // Si el campo viene como máscara (**********) o vacío, no actualizar la contraseña
        if ($password === '**********' || $password === '' || is_null($password)) {
            $password = null;
        }
        //dd($user_id, $username, $nombre, $apellido, $email, $password);
        try {
            $resultado = SQLConnection::backendStoreProcedure('users_backend', array('M',$user_id, null, null,null,$nombre,$apellido,$password, null,null,null,null));
            return response()->json(['success' => true, 'message' => 'Usuario actualizado correctamente.']);
        } catch (Throwable $e) {
            return response()->json(['success' => false, 'message' => 'Error al actualizar el usuario: ' . $e->getMessage()]);
        }
    }
    public static function actualizarLimitesApiKey() {
        $id_cliente = Request::Input("id_cliente");
        $id_api_key = Request::Input("id_api_key");
        $tope_diario = Request::Input("topediario");
        $tope_mensual = Request::Input("topemensual");
        $tope_anual = Request::Input("topeanual");
        $bloquea = Request::Input("bloquea");
        $vigencia = Request::Input("vigencia");
        $vigencia_formateada = Carbon::createFromFormat('Y-m-d', $vigencia)->format('d/m/Y');
        $ips = Request::Input("ips");
        $user_id = Session::get('usuario')->user_id;
        try {
            SQLConnection::backendStoreProcedure('apikey_abm',array('UPDAPIDATA',$id_cliente,0,0,0,1,'','1','0',null,'1',$id_api_key,null,$tope_diario,$tope_mensual,$tope_anual,$bloquea,$vigencia,1,$ips,null,null,null,null,null,$user_id,'0'));
        } catch (Throwable $e) {
            return response()->json(['success' => false, 'message' => 'Error al actualizar los límites de la API Key: ' . $e->getMessage()]);
        }
        return response()->json(['success' => true, 'message' => 'Límites de la API Key actualizados correctamente.', 'vigencia' => $vigencia_formateada]);
    }

    public static function crearUsuarioApiKey() {
        $form_data = Request::Input("formData");
        $id_cliente = $form_data['id_cliente'];
        $id_producto = $form_data['id_producto'];
        $user = Session::get('usuario')->user_id;
        $nombre = $form_data['nombre'];
        $apellido = $form_data['apellido'];
        $email = $form_data['email'];
        $username = $form_data['username'];
        $password = $form_data['password'];
        $valores = '0123456789QWERTYUIOPASDFGHJKLZXCVBNM';
        $nuevo_token =  substr(str_shuffle($valores),0,20);
        $sin_limite = isset($form_data['sin_limite']) ? $form_data['sin_limite'] : 0;
        $bloquea = isset($form_data['bloquea']) ? $form_data['bloquea'] : 0;
        $limite_diario = $sin_limite == 1 ? null : $form_data['limite_llamadas'];
        $limite_mensual = $sin_limite == 1 ? null : $form_data['limite_llamadas_mensuales'];
        $limite_anual = $sin_limite == 1 ? null : $form_data['limite_llamadas_anuales'];
        try{
            $create_user = SQLConnection::backendStoreProcedure('backend_abm_usuarios_productos', array('A',$id_cliente,null, $username, 'Y',$id_producto, 1, $email,$user,$nombre,$apellido,$password),'selectOne');
            if($create_user->error == 0){
                //HAY QUE EVALUAR SI EL PRODUCTO ES INFORME COMERCIAL PARA ITERAR POR SECCIONES EN CASO DE SERLO
                if($id_producto != 12){
                $secciones = 0;
                $create_usuario_producto = SQLConnection::backendStoreProcedure('backend_abm_usuarios_productos_secciones', array('A',$id_cliente, null, $username, $id_producto, $secciones, 1, null, null, $user));
                $new_user= SQLConnection::backendStoreProcedure('detalle_cliente_apis', array('obtener_nuevo_usuario', null, null, null, null, $username),'selectOne');
                $new_user_id= $new_user->user_id;
                //Insertar en tabla cliente-usuario
                SQLConnection::backendStoreProcedure('usuarios_servicios_abm', array('AGREGAR', $id_cliente, $new_user_id, 1, null, null, null, null, null, null, null, null, null, 0, 2, null, null, null));
                //Insertar la apikey
                SQLConnection::backendStoreProcedure('apikey_abm',array('ALTA', intval($id_cliente), 0, 0, 0, 0, '', '1', $id_producto, $new_user_id, '1', null, $nuevo_token, $limite_diario, $limite_mensual, $limite_anual, $bloquea, null, 1, null, null, null, null, null, null, $user, $id_producto));
                return response()->json(['success' => true, 'message' => 'Usuario creado correctamente.', 'nuevo_token' => $nuevo_token, 'nombre' => $nombre, 'apellido' => $apellido]);
            } else {
                return response()->json(['success' => false, 'message' => 'Error al crear el usuario: Producto tiene que asignar secciones que no se especificaron.']);
                }
            }
        } catch (Throwable $e) {
            return response()->json(['success' => false, 'message' => 'Error al crear el usuario: ' . $e->getMessage()]);
        }
    }

    public static function crearUsuarioExistenteApiKey(){
        $apynom = Request::Input("nombreApellido");
        $id_cliente = Request::Input("id_cliente");
        $id_producto = Request::Input("id_producto");
        $user_id = Request::Input("user_id");
        $username = Request::Input("username");
        $user = Session::get('usuario')->user_id;
        $limite_diario = Request::Input("limite_diario");
        $limite_mensual = Request::Input("limite_mensual");
        $limite_anual = Request::Input("limite_anual");
        $sin_limite = Request::Input("sin_limite");
        $bloquea = Request::Input("bloquea");
        $valores = '0123456789QWERTYUIOPASDFGHJKLZXCVBNM';
        $nuevo_token =  substr(str_shuffle($valores),0,20);
        //HAY QUE EVALUAR SI EL PRODUCTO ES INFORME COMERCIAL PARA ITERAR POR SECCIONES EN CASO DE SERLO
        try {
            if ($id_producto != 12) {
                $secciones = 0;
                $mensaje_extra = "";

                // BLOQUE 1: Intentar asignar el producto al usuario
                try {
                    $create_usuario_producto = SQLConnection::backendStoreProcedure(
                        'backend_abm_usuarios_productos_secciones',
                        array('A', $id_cliente, null, $username, $id_producto, $secciones, 1, null, null, $user)
                    );
                } catch (Throwable $e_sql) {
                    // Verificamos si el error es por clave duplicada (SQLSTATE 23000 o mensaje específico)
                    // El mensaje 'Violation of PRIMARY KEY' es lo que nos interesa filtrar
                    if (strpos($e_sql->getMessage(), 'Violation of PRIMARY KEY') !== false || strpos($e_sql->getMessage(), '2627') !== false) {
                        // Si ya existe, NO detenemos el proceso. Solo guardamos un aviso.
                        $mensaje_extra = " (El usuario ya tenía el producto asignado previamente).";
                    } else {
                        // Si es CUALQUIER OTRO error (conexión, sintaxis, etc), lo lanzamos para que falle normal
                        throw $e_sql;
                    }
                }

                // BLOQUE 2: Crear el token (Se ejecuta aunque el Bloque 1 haya dado error de duplicado)
                SQLConnection::backendStoreProcedure(
                    'apikey_abm',
                    array('ALTA', intval($id_cliente), 0, 0, 0, 0, '', '1', $id_producto, $user_id, '1', null, $nuevo_token, $limite_diario, $limite_mensual, $limite_anual, $bloquea, null, 1, null, null, null, null, null, null, $user, $id_producto)
                );

                return response()->json([
                    'success' => true,
                    'message' => 'Proceso completado correctamente.' . $mensaje_extra,
                    'nuevo_token' => $nuevo_token,
                    'nombre' => $apynom
                ]);

            } else {
                return response()->json(['success' => false, 'message' => 'Error al crear el usuario: Producto tiene que asignar secciones que no se especificaron.']);
            }
        } catch (Throwable $e) {
            return response()->json(['success' => false, 'message' => 'Error crítico al crear el usuario: ' . $e->getMessage()]);
        }
    }

    public static function abrirModalNuevoUsuario() {
        $cliente = Request::Input("id_cliente");
        $nombre_cliente = Request::Input("nombre_cliente");
        $producto = Request::Input("nombre_producto");
        $id_producto = Request::Input("id_producto");
        return view('dashboard-api/modal_nuevo_usuario', compact('cliente', 'nombre_cliente', 'producto', 'id_producto'));
    }

    public static function abrirModalUsuarioExistente() {
        $cliente = Request::Input("id_cliente");
        $nombre_cliente = Request::Input("nombre_cliente");
        $producto = Request::Input("nombre_producto");
        $id_producto = Request::Input("id_producto");
        $usuarios = SQLConnection::backendStoreProcedure('detalle_cliente_apis', array('usuarios_apis_productos',$cliente, $id_producto), 'select');
        return view('dashboard-api/modal_usuario_existente', compact('cliente', 'nombre_cliente', 'producto', 'id_producto', 'usuarios'));
    }

    public static function descargarExcelUsuarios(){
        $id_cliente = Request::Input("id_cliente");
        $id_producto = Request::Input("id_producto");
        $data = SQLConnection::backendStoreProcedure('detalle_cliente_apis', array('usuarios_apis_limpio',$id_cliente, $id_producto), 'select');

        if (empty($data)) {
            return response()->json(['error' => 'No hay datos para exportar'], 404);
        }

        // Directorio de salida
        $dir = env('PATH_EXCEL_API', storage_path('app/public/excel_api'));
        if (!is_dir($dir)) mkdir($dir, 0777, true);

        // Preparar datos en formato de matriz
            $arrayData = [];

            // Encabezados
            $headers = array_keys((array)$data[0]);
            $arrayData[] = $headers;

            // Filas
            foreach ($data as $fila) {
                $fila = (array)$fila;
                $arrayData[] = array_values($fila);
            }

            // Generar archivo Excel
            $xlsx = SimpleXLSXGen::fromArray($arrayData);

            // Guardar en storage
            $dir = env('PATH_EXCEL_API', storage_path('app/public/excel_api'));
            if (!is_dir($dir)) mkdir($dir, 0777, true);

            $fileName = 'Usuarios_' . $id_cliente . '_' . date('Ymd_His') . '.xlsx';
            $filePath = $dir . '/' . $fileName;

            $xlsx->saveAs($filePath);

            // Descargar y eliminar después
            return response()->download($filePath, $fileName, [
                'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            ])->deleteFileAfterSend(true);
        }
   
public static function descargarExcelHistorialConsumo()
{
    $cliente = Request::input("id_cliente");
    $id_producto = Request::input("id_producto");

    // Trae un solo registro
    $data = SQLConnection::backendStoreProcedure(
        'detalle_cliente_apis',
        ['obtener_consumos', $cliente, $id_producto],
        'selectOne'
    );

    if (empty($data)) {
        return response()->json(['error' => 'No hay datos de consumo'], 404);
    }

    // Convertir a array simple
    $data = (array) $data;

    // ---------- CABECERAS ----------
    $cabeceras = [];
    $valores = [];

    for ($i = 1; $i <= 12; $i++) {

        $keyMes = "mes_anterior" . ($i == 1 ? "" : $i);
        $keyConsumo = "consumo_mes_anterior" . ($i == 1 ? "" : $i);

        // Mes: si viene null → "-"
        $cabeceras[] = $data[$keyMes] ?? "-";

        // Consumo: si viene null → 0
        $valores[] = $data[$keyConsumo] ?? 0;
    }

    // Preparar Excel
    $arrayData = [];
    $arrayData[] = $cabeceras;
    $arrayData[] = $valores;

    $dir = env('PATH_EXCEL_API', storage_path('app/public/excel_api'));

    if (!is_dir($dir)) mkdir($dir, 0777, true);

    $fileName = 'HistorialConsumo_' . $cliente . '_' . date('Ymd_His') . '.xlsx';
    $filePath = $dir . '/' . $fileName;

    $xlsx = SimpleXLSXGen::fromArray($arrayData);
    $xlsx->saveAs($filePath);

    return response()->download(
        $filePath,
        $fileName,
        ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
    )->deleteFileAfterSend(true);
}


    public static function guardarCambiosContrato() {
        //dd($_POST);
        $datos = Request::input("datos");
        $id_cliente = Request::Input("id_cliente");
        $id_producto = Request::Input("id_producto");
        $user_id = Session::get('usuario')->user_id;
        $limite_consumo = Request::Input("datos.tope-contrato");
        $minimo_facturable = Request::Input("datos.minimo-facturable");
        $moneda = Request::Input("datos.moneda");
        $precio_unitario = Request::Input("datos.precio");
        $tipo_contrato = Request::Input("datos.tipo-contrato");
        $escalonado = Request::Input("datos.escalonado");
        $tipo_escalonado = Request::Input("tipo_escalonado") ?? 1;
        // Array donde vas a acumular los escalones
        $precios_escalonados = [];
        $escalones_eliminados = Request::Input("escalonesEliminados") ?? [];
$tango_code = Request::Input("datos.tango-code");

        // Recorremos todas las keys del array datos
        foreach ($datos as $key => $value) {

            // Coincide con desde-N ?
            if (preg_match('/^desde-(\d+)$/', $key, $match)) {
                $i = $match[1]; // número del escalón
                $precios_escalonados[$i]['desde'] = $value;
            }

            // Coincide con hasta-N ?
            if (preg_match('/^hasta-(\d+)$/', $key, $match)) {
                $i = $match[1];
                // Si el valor viene vacío o explícitamente dice "infinito", lo guardamos como NULL
                if ($value === '' || strtolower($value) === 'infinito') {
                    $precios_escalonados[$i]['hasta'] = null;
                } else {
                    $precios_escalonados[$i]['hasta'] = $value;
                }
            }

            // Coincide con precio-N ?
            if (preg_match('/^precio-(\d+)$/', $key, $match)) {
                $i = $match[1];
                $precios_escalonados[$i]['precio'] = $value;
            }

            // Coincide con id-escalon-N ?
            if (preg_match('/^id-escalon-(\d+)$/', $key, $match)) {
                $i = $match[1];
                $precios_escalonados[$i]['id-escalon'] = $value;
            }
        }

        // Ordeno por índice (1,2,3,…)
        ksort($precios_escalonados);

        // Reindexo (opcional) para obtener 0,1,2,…
        $precios_escalonados = array_values($precios_escalonados);
        try {
            //Actualizo contrato principal
            $update_contrato = SQLConnection::backendStoreProcedure('clientes_productos_contrato', array('update', $id_cliente, $id_producto, $limite_consumo, $moneda, $precio_unitario,$tipo_contrato, null, $escalonado, $user_id, null, $minimo_facturable, $tipo_escalonado,$tango_code));
            $ids_nuevos = [];
            //Recorro escalones y los inserto en caso de que no tengan ID
            //dd($precios_escalonados);
            foreach ($precios_escalonados as $numero => $escalon) {
                $desde = $escalon['desde'] ?? null;
                $hasta = $escalon['hasta'];
                $precio = $escalon['precio'] ?? null;
                $id_escalon = $escalon['id-escalon'] ?? null;

                if ($desde !== null && $precio !== null && $id_escalon === null) {
                    // Llamada al procedimiento almacenado para cada escalón
                    $insert = SQLConnection::backendStoreProcedure('precios_escalonados_', array('I', null, $id_cliente, $id_producto, $desde, $hasta, $precio, $user_id), 'selectOne');
                    $ids_nuevos[] = [
                        'numero' => $numero + 1,
                        'id' => (int) $insert->nuevo_id_escalon
                    ];
                } elseif ($id_escalon !== null) {
                    // Actualizo escalón existente
                    SQLConnection::backendStoreProcedure('precios_escalonados_', array('U', $id_escalon, $id_cliente, $id_producto, $desde, $hasta, $precio, $user_id));
                }
            }
            // Elimino escalones marcados para eliminación
            foreach ($escalones_eliminados as $id_escalon_eliminar) {
                SQLConnection::backendStoreProcedure('precios_escalonados_', array('D', $id_escalon_eliminar, null, null, null, null, null, $user_id));
            }
        } catch (Throwable $e) {
            return response()->json(['success' => false, 'message' => 'Error al actualizar el contrato: ' . $e->getMessage()]);
        }
        //dd($ids_nuevos);
        return response()->json(['success' => true, 'message' => 'Contrato actualizado correctamente.', 'ids_nuevos' => $ids_nuevos]);
    }


}
