<?php

require_once __DIR__ . '/../includes/logger.php';
require_once __DIR__ . '/../src/Translate.php';

class TelegramSender
{
    private $botToken;
    private $apiUrl = 'https://api.telegram.org/bot';
    private $pdo;
    private $baseUrl; // Nueva propiedad para almacenar BOT_BASE_URL

    public function __construct($botToken, $pdo, string $baseUrl)
    {
        $this->botToken = $botToken;
        $this->pdo = $pdo;
        $this->baseUrl = rtrim($baseUrl, '/'); // Asegurarse de que no tenga una barra al final
    }

    public function sendMessage($chatId, $content, $options = [], $addTranslateButton = false, $messageLanguage = 'es', $originalFullContent = null)
    {
        try {
            custom_log("Iniciando envío de mensaje a chat $chatId");
            if (empty(trim($content))) {
                custom_log("Error: Contenido vacío para el chat $chatId");
                return false;
            }

            // Obtener el language_code del destinatario
            $recipientLang = 'es'; // Idioma por defecto
            try {
                $stmt = $this->pdo->prepare("SELECT language_code FROM recipients WHERE platform_id = ? AND platform = 'telegram'");
                $stmt->execute([$chatId]);
                $dbLang = $stmt->fetchColumn();
                if ($dbLang) {
                    $recipientLang = $dbLang;
                }
            } catch (PDOException $e) {
                custom_log("Error al obtener language_code del destinatario: " . $e->getMessage());
            }
            custom_log("[DEBUG] Idioma del destinatario ($chatId): " . $recipientLang);

            $parts = $this->parseContent($content);
            if (empty($parts)) {
                custom_log("No se pudo parsear el contenido para el chat $chatId");
                return false;
            }

            $messageIds = [];
            $all_sent_successfully = true;
            $totalParts = count($parts);
            custom_log("Procesando $totalParts partes del mensaje para el chat $chatId");

            foreach ($parts as $index => $part) {
                $isLastPart = ($index === $totalParts - 1);
                $partOptions = $isLastPart ? $options : [];

                try {
                    custom_log("Procesando parte " . ($index + 1) . "/$totalParts de tipo: " . $part['type']);
                    $response = null;
                    $partSuccess = false;

                    if ($part['type'] === 'text') {
                        // Asegurarse de que el parámetro addTranslateButton se pase correctamente
                        if ($isLastPart && $addTranslateButton) {
                            $partOptions['add_translate_button'] = true;
                        }
                        $response = $this->sendText($chatId, $part['content'], $partOptions);
                        $partSuccess = ($response && ($response['ok'] ?? false));
                    } elseif ($part['type'] === 'image') {
                        // Verificar la URL de la imagen antes de intentar enviarla
                        $imageUrl = $part['url'] ?? '';
                        custom_log("Verificando URL de imagen: " . $imageUrl);

                        if ($this->isValidImageUrl($imageUrl)) {
                            // Asegurarse de que el parámetro addTranslateButton se pase correctamente
                            if ($isLastPart && $addTranslateButton) {
                                $partOptions['add_translate_button'] = true;
                            }
                            $response = $this->sendPhoto($chatId, $imageUrl, $part['caption'] ?? '', $partOptions);
                            $partSuccess = ($response && ($response['ok'] ?? false));
                        } else {
                            $errorMsg = "URL de imagen no válida o inaccesible: " . $imageUrl;
                            custom_log($errorMsg);

                            // Enviar un mensaje de error al chat
                            $errorText = "⚠️ No se pudo cargar una imagen: " . basename($imageUrl) . "\n";
                            if (!empty($part['caption'])) {
                                $errorText .= "📝 " . $part['caption'] . "\n";
                            }
                            $errorText .= "🔗 " . $imageUrl;

                            $this->sendText($chatId, $errorText);
                            $partSuccess = false;
                        }
                    }

                    if ($partSuccess) {
                        if (isset($response['result']['message_id'])) {
                            $messageIds[] = [
                                'success' => true,
                                'message_id' => $response['result']['message_id'],
                                'type' => $part['type']
                            ];
                            
                        }
                    } else {
                        $error = $response['description'] ?? 'Error desconocido al procesar la parte del mensaje';
                        custom_log("Error al enviar parte " . ($index + 1) . "/$totalParts: " . json_encode($error));

                        // Agregar el error a los IDs de mensaje para referencia
                        $messageIds[] = [
                            'success' => false,
                            'error' => $error,
                            'type' => $part['type'],
                            'content' => $part['type'] === 'image' ? $part['url'] : substr($part['content'] ?? '', 0, 100) . '...'
                        ];

                        $all_sent_successfully = false;
                    }
                } catch (Exception $e) {
                    custom_log("Excepción al procesar parte: " . $e->getMessage());
                    $all_sent_successfully = false;
                }

                if (!$isLastPart) {
                    usleep(500000); // 500ms de pausa
                }
            }

            // Si hubo algún error, devolver información detallada
            if (!$all_sent_successfully) {
                $failedParts = array_filter($messageIds, function($msg) {
                    return isset($msg['success']) && $msg['success'] === false;
                });
                
                custom_log("No se pudieron enviar " . count($failedParts) . " partes del mensaje");
                
                // Si no se pudo enviar ninguna parte, devolver falso
                if (count($failedParts) === count($messageIds)) {
                    return false;
                }
            }
            
            // Obtener los mensajes enviados exitosamente
            $sentMessages = array_filter($messageIds, function($msg) { 
                return !empty($msg['success']) && !empty($msg['message_id']); 
            });
            
            // Guardar el mensaje saliente en telegram_interactions con el idioma del remitente
            // Esto se hace una vez, después de que todas las partes del mensaje han sido enviadas.
            if ($all_sent_successfully && !empty($messageIds)) {
                $lastSentMessage = end($messageIds);
                $lastMessageId = $lastSentMessage['message_id'] ?? null;
                $contentToSave = $originalFullContent ?? $content; // Use originalFullContent or the assembled content

                if ($lastMessageId) {
                    // Ensure contentToSave is not NULL
                    if ($contentToSave === null) {
                        $contentToSave = '';
                    }
                    try {
                        $stmt = $this->pdo->prepare("INSERT INTO telegram_interactions (chat_id, message_text, direction, telegram_message_id, language_code) VALUES (?, ?, 'out', ?, ?)");
                        $stmt->execute([$chatId, $contentToSave, $lastMessageId, $messageLanguage]);
                        custom_log("[DEBUG] Mensaje completo guardado con idioma: $messageLanguage y message_id: " . $lastMessageId);
                    } catch (PDOException $e) {
                        custom_log("Error al guardar mensaje saliente en telegram_interactions (después del loop): " . $e->getMessage());
                    }
                }
            }

            // Lógica para los botones de traducción basados en supported_languages
            if ($addTranslateButton && !empty($sentMessages)) {
                $lastMessage = end($sentMessages);
                $lastMessageId = $lastMessage['message_id'] ?? null;

                if ($lastMessageId) {
                    $messageLang = strtolower($messageLanguage);
                    $buttons = [];
                    try {
                        $stmt = $this->pdo->query("SELECT language_code, flag_emoji FROM supported_languages WHERE is_active = 1 ORDER BY language_name ASC");
                        $langs = $stmt->fetchAll(PDO::FETCH_ASSOC);

                        foreach ($langs as $lang) {
                            $code = strtolower($lang['language_code']);
                            if ($code === $messageLang) continue; // no traducir al mismo idioma
                            $flag = $lang['flag_emoji'] ?: '';
                            $buttons[] = [
                                'text' => $flag !== '' ? $flag : strtoupper($code),
                                'callback_data' => 'translate:' . $lastMessageId . ':' . $code
                            ];
                        }
                    } catch (Exception $e) {
                        custom_log('[DEBUG] No se pudieron obtener idiomas activos para botones: ' . $e->getMessage());
                    }

                    if (!empty($buttons)) {
                        // Armar filas de hasta 6 botones por fila para no saturar
                        $inline = [];
                        $row = [];
                        foreach ($buttons as $i => $btn) {
                            $row[] = $btn;
                            if (count($row) >= 6) {
                                $inline[] = $row;
                                $row = [];
                            }
                        }
                        if (!empty($row)) $inline[] = $row;

                        $keyboard = ['inline_keyboard' => $inline];
                        custom_log('[DEBUG] Enviando ' . count($buttons) . ' botones de traducción para mensaje ' . $lastMessageId);
                        $this->editMessageReplyMarkup($chatId, $lastMessageId, json_encode($keyboard));
                    } else {
                        custom_log('[DEBUG] No hay idiomas activos distintos a ' . $messageLang . ' para mostrar botones.');
                    }
                } else {
                    custom_log('[DEBUG] No se muestra botón de traducción. Razón: Falta message ID');
                }
            }

            // Devolver los IDs de los mensajes que se enviaron correctamente
            return $messageIds;
        } catch (Exception $e) {
            custom_log("Error crítico en sendMessage para el chat $chatId: " . $e->getMessage());
            return false;
        }
    }

    public function deleteMessage($chatId, $messageId)
    {
        $response = $this->apiRequest('deleteMessage', [
            'chat_id' => $chatId,
            'message_id' => $messageId
        ]);
        if (!isset($response['ok']) || $response['ok'] !== true) {
            throw new Exception("Error de Telegram al eliminar");
        }
        return true;
    }

    public function editMessageText($chatId, $messageId, $text, $options = [])
    {
        $params = array_merge([
            'chat_id' => $chatId,
            'message_id' => $messageId,
            'text' => $text
        ], $options);

        return $this->apiRequest('editMessageText', $params);
    }

    public function editMessageReplyMarkup($chatId, $messageId, $replyMarkup)
    {
        $params = [
            'chat_id' => $chatId,
            'message_id' => $messageId
        ];

        // Solo agregar reply_markup si no está vacío
        if (!empty($replyMarkup)) {
            $params['reply_markup'] = $replyMarkup;
        } else {
            // Para eliminar el teclado, necesitamos enviar un objeto JSON vacío
            $params['reply_markup'] = '{"inline_keyboard":[]}';
        }

        return $this->apiRequest('editMessageReplyMarkup', $params);
    }

    public function answerCallbackQuery($callbackQueryId, $options = [])
    {
        $params = array_merge([
            'callback_query_id' => $callbackQueryId
        ], $options);

        return $this->apiRequest('answerCallbackQuery', $params);
    }

    private function parseContent($html)
    {
        $parts = [];
        if (empty(trim($html))) return [];

        if (strip_tags($html) === $html) {
            return [['type' => 'text', 'content' => $html]];
        }

        preg_match_all('/(<img[^>]+src=[\'\"]([^\'\"]+)[\'\"][^>]*>)/i', $html, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);

        if (empty($matches)) {
            return [['type' => 'text', 'content' => $html]];
        }

        $last_pos = 0;
        foreach ($matches as $match) {
            $full_match = $match[0][0];
            $image_url = $match[2][0];
            $offset = $match[0][1];

            $text_before = substr($html, $last_pos, $offset - $last_pos);
            if (!empty(trim($text_before))) {
                $parts[] = ['type' => 'text', 'content' => trim($text_before)];
            }

            // Convertir rutas relativas a absolutas si es necesario
            if (!preg_match('/^https?:\/\//', $image_url)) {
                $base_url = $this->baseUrl;
                $image_url = $base_url . '/' . ltrim($image_url, '/');
                custom_log("[DEBUG] URL de imagen convertida: " . $image_url);
                
                // Verificar si la URL es accesible
                $headers = @get_headers($image_url);
                if ($headers && strpos($headers[0], '200') === false) {
                    error_log("[ERROR] La imagen no es accesible en: " . $image_url);
                } else {
                    error_log("[DEBUG] La imagen es accesible en: " . $image_url);
                }
            }
            $parts[] = ['type' => 'image', 'url' => $image_url, 'caption' => ''];

            $last_pos = $offset + strlen($full_match);
        }

        $remaining_text = substr($html, $last_pos);
        if (!empty(trim($remaining_text))) {
            $parts[] = ['type' => 'text', 'content' => trim($remaining_text)];
        }

        return $parts;
    }

    private function splitLongText($text, $maxLength = 4000)
    {
        if (mb_strlen($text) <= $maxLength) return [$text];
        $parts = [];
        $paragraphs = preg_split('/\n\s*\n/', $text);
        $currentPart = '';
        foreach ($paragraphs as $paragraph) {
            if ((mb_strlen($currentPart) + mb_strlen($paragraph) + 2) <= $maxLength) {
                $currentPart .= (empty($currentPart) ? '' : "\n\n") . $paragraph;
            } else {
                if (!empty($currentPart)) $parts[] = $currentPart;
                $currentPart = $paragraph;
                while (mb_strlen($currentPart) > $maxLength) {
                    $chunk = mb_substr($currentPart, 0, $maxLength);
                    $parts[] = $chunk;
                    $currentPart = mb_substr($currentPart, $maxLength);
                }
            }
        }
        if (!empty($currentPart)) $parts[] = $currentPart;
        return $parts;
    }

    /**
     * Envía un mensaje de texto a un chat de Telegram, manejando mensajes largos y rate limiting
     * 
     * @param int|string $chatId ID del chat de Telegram
     * @param string $text Texto a enviar
     * @param array $options Opciones adicionales para el mensaje
     * @param int $retryCount Número de reintentos actual (uso interno)
     * @param int $maxRetries Número máximo de reintentos
     * @return array|null Respuesta de la API o null en caso de error
     */
    private function sendText($chatId, $text, $options = [], $retryCount = 0, $maxRetries = 3)
    {
        // Asegurarse de que $options sea un array
        if (!is_array($options)) {
            custom_log("ADVERTENCIA: Las opciones no son un array. Valor recibido: " . print_r($options, true));
            $options = [];
        }
        
        // Verificar si se debe agregar el botón de traducción
        $addTranslateButton = $options['add_translate_button'] ?? false;
        unset($options['add_translate_button']); // Eliminar para evitar conflictos
        
        // Configuración de depuración
        $debug = $options['debug'] ?? false;
        unset($options['debug']);
        
        // Limitar la longitud de los mensajes de depuración
        $debugText = $debug ? substr($text, 0, 200) : '';
        $logPrefix = "[SEND_TEXT] [Chat: $chatId]";
        
        custom_log("$logPrefix === INICIO ===");
        custom_log("$logPrefix Tamaño del texto: " . mb_strlen($text, 'UTF-8') . " caracteres");
        
        if ($debug) {
            custom_log("$logPrefix Texto (primeros 200 caracteres): $debugText");
            custom_log("$logPrefix Opciones: " . json_encode($options));
            custom_log("$logPrefix Mostrar botón de traducción: " . ($addTranslateButton ? 'Sí' : 'No'));
        }
        
        // Limpiar el texto de etiquetas HTML innecesarias
        $clean_text = trim(strip_tags($text));
        if (empty($clean_text)) {
            custom_log("$logPrefix ERROR: El texto está vacío después de limpiar etiquetas");
            return null;
        }
        
        // Dividir el texto en partes manejables
        $textParts = $this->splitLongText($clean_text);
        $totalParts = count($textParts);
        $allResponses = [];
        $allSuccessful = true;
        
        custom_log("$logPrefix Texto dividido en $totalParts partes");

        foreach ($textParts as $index => $part) {
            $isLastPart = ($index === $totalParts - 1);
            $partNumber = $index + 1;
            
            // Configurar parámetros básicos del mensaje
            $params = [
                'chat_id' => $chatId,
                'text' => $part,
                'disable_web_page_preview' => true,
                'parse_mode' => 'HTML',
                'disable_notification' => $options['disable_notification'] ?? false
            ];

            // Agregar opciones adicionales solo a la última parte del mensaje
            if ($isLastPart) {
                // Mantener solo las opciones permitidas por la API de Telegram
                $allowedOptions = [
                    'reply_markup', 'reply_to_message_id', 'allow_sending_without_reply',
                    'entities', 'protect_content', 'message_thread_id'
                ];
                
                foreach ($allowedOptions as $option) {
                    if (isset($options[$option])) {
                        $params[$option] = $options[$option];
                    }
                }
                
                if ($debug) {
                    custom_log("$logPrefix [Parte $partNumber/$totalParts] Opciones aplicadas: " . 
                              json_encode(array_keys($params)));
                }
            }
            
            // Intentar enviar la parte del mensaje con reintentos
            $attempt = 0;
            $maxAttempts = 3;
            $response = null;
            
            while ($attempt < $maxAttempts) {
                $attempt++;
                custom_log("$logPrefix [Parte $partNumber/$totalParts] Intento $attempt de $maxAttempts");
                
                $response = $this->apiRequest('sendMessage', $params);
                
                // Verificar si la solicitud fue exitosa
                if (!empty($response) && ($response['ok'] ?? false)) {
                    $allResponses[] = $response;
                    
                    if ($debug) {
                        $msgId = $response['result']['message_id'] ?? 'desconocido';
                        custom_log("$logPrefix [Parte $partNumber/$totalParts] Enviado exitosamente (ID: $msgId)");
                    }
                    
                    // Pequeña pausa entre mensajes para evitar rate limiting
                    if (!$isLastPart) {
                        usleep(500000); // 0.5 segundos
                    }
                    
                    break; // Salir del bucle de reintentos
                } else {
                    $errorCode = $response['error_code'] ?? 'desconocido';
                    $errorDesc = $response['description'] ?? 'Error desconocido';
                    
                    custom_log("$logPrefix [Parte $partNumber/$totalParts] Error $errorCode: $errorDesc");
                    
                    // Manejar errores específicos
                    if (strpos($errorDesc, 'Too Many Requests') !== false) {
                        $retryAfter = $response['parameters']['retry_after'] ?? 5; // Por defecto 5 segundos
                        custom_log("$logPrefix Rate limit alcanzado. Reintentando en $retryAfter segundos...");
                        sleep($retryAfter);
                        continue; // Reintentar
                    }
                    
                    // Para otros errores, esperar un tiempo antes de reintentar
                    if ($attempt < $maxAttempts) {
                        $backoffTime = pow(2, $attempt); // Retroceso exponencial
                        custom_log("$logPrefix Reintentando en $backoffTime segundos...");
                        sleep($backoffTime);
                    } else {
                        custom_log("$logPrefix Se agotaron los reintentos para esta parte del mensaje");
                        $allSuccessful = false;
                    }
                }
            }
            
            // Si no se pudo enviar después de los reintentos, marcar como fallido
            if (empty($response) || !($response['ok'] ?? false)) {
                $allSuccessful = false;
                
                // Si es un error crítico, detener el envío de las partes restantes
                $errorCode = $response['error_code'] ?? 0;
                $criticalErrors = [400, 403, 404]; // Errores que indican que no tiene sentido seguir intentando
                
                if (in_array($errorCode, $criticalErrors)) {
                    custom_log("$logPrefix Error crítico ($errorCode). Deteniendo el envío de las partes restantes.");
                    break;
                }
            }
        }
        
        // Devolver la última respuesta exitosa o null si hubo errores
        $result = $allSuccessful ? end($allResponses) : null;
        custom_log("$logPrefix === FIN === " . ($allSuccessful ? 'ÉXITO' : 'CON ERRORES'));
        
        return $result;
    }

    /**
     * Envía una foto a un chat de Telegram con manejo de errores y reintentos
     * 
     * @param int|string $chatId ID del chat de Telegram
     * @param string $photoUrl URL de la foto a enviar
     * @param string $caption Texto opcional para la foto
     * @param array $options Opciones adicionales para el mensaje
     * @param int $retryCount Número de reintentos actual (uso interno)
     * @param int $maxRetries Número máximo de reintentos
     * @return array|bool Respuesta de la API o false en caso de error
     */
    private function sendPhoto($chatId, $photoUrl, $caption = '', $options = [], $retryCount = 0, $maxRetries = 3)
    {
        $logPrefix = "[SEND_PHOTO] [Chat: $chatId]";
        custom_log("$logPrefix === INICIO ===");
        
        try {
            // Validar y normalizar la URL de la imagen
            $photoUrl = $this->normalizeAndValidateImageUrl($photoUrl, $logPrefix);
            
            // Si la URL no es válida, intentar enviar un mensaje de error
            if ($photoUrl === false) {
                $fallbackMessage = $this->createPhotoErrorFallback($photoUrl, $caption, "URL de imagen no válida o inaccesible");
                return $this->sendText($chatId, $fallbackMessage, $options);
            }
            
            // Configurar parámetros básicos para el envío de la foto
            $params = [
                'chat_id' => $chatId,
                'photo' => $photoUrl,
                'caption' => $caption,
                'parse_mode' => 'HTML',
                'disable_notification' => $options['disable_notification'] ?? false
            ];
            
            // Agregar opciones adicionales permitidas por la API de Telegram
            $allowedOptions = [
                'reply_markup', 'reply_to_message_id', 'allow_sending_without_reply',
                'protect_content', 'message_thread_id'
            ];
            
            foreach ($allowedOptions as $option) {
                if (isset($options[$option])) {
                    $params[$option] = $options[$option];
                }
            }
            
            // Realizar el envío con manejo de reintentos
            $result = $this->sendPhotoWithRetry($params, $photoUrl, $retryCount, $maxRetries, $logPrefix);
            
            // Si el envío falló después de los reintentos, enviar un mensaje de error
            if ($result === false || (isset($result['ok']) && !$result['ok'])) {
                $errorDesc = $result['description'] ?? 'Error desconocido';
                custom_log("$logPrefix Error al enviar foto después de $maxRetries intentos: $errorDesc");
                
                $fallbackMessage = $this->createPhotoErrorFallback($photoUrl, $caption, $errorDesc);
                return $this->sendText($chatId, $fallbackMessage, $options);
            }
            
            custom_log("$logPrefix Foto enviada exitosamente");
            return $result;
            
        } catch (Exception $e) {
            custom_log("$logPrefix Excepción: " . $e->getMessage() . " - URL: $photoUrl");
            
            // En caso de error inesperado, intentar enviar un mensaje de texto
            $fallbackMessage = $this->createPhotoErrorFallback(
                $photoUrl, 
                $caption, 
                "Error inesperado: " . $e->getMessage()
            );
            
            return $this->sendText($chatId, $fallbackMessage, $options);
        } finally {
            custom_log("$logPrefix === FIN ===");
        }
    }
    
    /**
     * Normaliza y valida una URL de imagen, convirtiendo URLs relativas a absolutas si es necesario
     * 
     * @param string $url URL de la imagen a normalizar y validar
     * @param string $logPrefix Prefijo para los mensajes de log
     * @return string|false URL normalizada o false si no es válida
     */
    private function normalizeAndValidateImageUrl($url, $logPrefix = '')
    {
        if (empty($url)) {
            custom_log("${logPrefix} URL de imagen vacía");
            return false;
        }
        
        // Si la URL es relativa, intentar convertirla a absoluta
        if (!preg_match('/^https?:\/\//i', $url)) {
            $baseUrl = $this->baseUrl;
            $absoluteUrl = $baseUrl . '/' . ltrim($url, '/');
            custom_log("${logPrefix} URL relativa detectada, convirtiendo a absoluta: $absoluteUrl");
            $url = $absoluteUrl;
        }
        
        // Verificar si la URL es accesible y es una imagen
        if ($this->isValidImageUrl($url)) {
            custom_log("${logPrefix} URL de imagen válida: $url");
            return $url;
        }
        
        custom_log("${logPrefix} URL de imagen no válida o inaccesible: $url");
        return false;
    }
    
    /**
     * Crea un mensaje de error estandarizado para cuando falla el envío de una foto
     * 
     * @param string $photoUrl URL de la foto que falló
     * @param string $caption Texto de la foto (opcional)
     * @param string $error Descripción del error
     * @return string Mensaje de error formateado
     */
    private function createPhotoErrorFallback($photoUrl, $caption, $error)
    {
        $message = "⚠️ *Error al procesar la imagen*\n";
        $message .= "\n🔗 *URL*: " . (strlen($photoUrl) > 100 ? substr($photo_url, 0, 100) . '...' : $photoUrl);
        
        if (!empty($error)) {
            $message .= "\n\n❌ *Error*: $error";
        }
        
        if (!empty($caption)) {
            $message .= "\n\n📝 *Descripción*: " . 
                       (mb_strlen($caption) > 200 ? mb_substr($caption, 0, 200) . '...' : $caption);
        }
        
        return $message;
    }
    
    /**
     * Envía una foto a Telegram con manejo de reintentos y rate limiting
     * 
     * @param array $params Parámetros para la API de Telegram
     * @param string $photoUrl URL de la foto (solo para registro)
     * @param int $retryCount Número de reintentos actual
     * @param int $maxRetries Número máximo de reintentos
     * @param string $logPrefix Prefijo para los mensajes de log
     * @return array|false Respuesta de la API o false en caso de error
     */
    private function sendPhotoWithRetry($params, $photoUrl, $retryCount = 0, $maxRetries = 3, $logPrefix = '')
    {
        $attempt = 0;
        $lastError = null;
        
        while ($attempt <= $maxRetries) {
            $attempt++;
            custom_log("${logPrefix} Intento $attempt de $maxRetries - Enviando foto...");
            
            $response = $this->apiRequest('sendPhoto', $params);
            
            // Si la solicitud fue exitosa, devolver la respuesta
            if (!empty($response) && ($response['ok'] ?? false)) {
                custom_log("${logPrefix} Foto enviada exitosamente en el intento $attempt");
                return $response;
            }
            
            // Si hay un error, registrarlo y verificar si se debe reintentar
            $errorCode = $response['error_code'] ?? 'desconocido';
            $errorDesc = $response['description'] ?? 'Error desconocido';
            $lastError = $errorDesc;
            
            custom_log("${logPrefix} Error en el intento $attempt: [$errorCode] $errorDesc");
            
            // Si es un error de rate limit, esperar el tiempo indicado
            if (isset($response['parameters']['retry_after'])) {
                $retryAfter = $response['parameters']['retry_after'];
                custom_log("${logPrefix} Rate limit alcanzado. Esperando $retryAfter segundos...");
                sleep($retryAfter);
                continue;
            }
            
            // Para otros errores, esperar un tiempo antes de reintentar (backoff exponencial)
            if ($attempt < $maxRetries) {
                $backoffTime = pow(2, $attempt); // 2, 4, 8, 16... segundos
                custom_log("${logPrefix} Reintentando en $backoffTime segundos...");
                sleep($backoffTime);
            }
        }
        
        // Si llegamos aquí, se agotaron los reintentos
        custom_log("${logPrefix} Se agotaron los $maxRetries intentos. Último error: $lastError");
        return $response ?? ['ok' => false, 'description' => $lastError ?? 'Error desconocido'];
    }
    
    /**
     * Verifica si una URL es una imagen accesible
     */
    private function isValidImageUrl($url)
    {
        if (empty($url)) {
            custom_log("[DEBUG] isValidImageUrl: URL vacía.");
            return false;
        }

        if (!preg_match('/^https?:\/\//', $url)) {
            $baseUrl = $this->baseUrl;
            $url = $baseUrl . '/' . ltrim($url, '/');
            custom_log("[DEBUG] isValidImageUrl: URL convertida a absoluta: " . $url);
        }

        if (!filter_var($url, FILTER_VALIDATE_URL)) {
            custom_log("[DEBUG] isValidImageUrl: URL no válida después de la conversión: " . $url);
            return false;
        }

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_NOBODY, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HEADER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_MAXREDIRS, 3);
        curl_setopt($ch, CURLOPT_TIMEOUT, 5);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        
        $headers = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
        curl_close($ch);

        custom_log("[DEBUG] isValidImageUrl: Verificación con cURL para " . $url . " - Código HTTP: " . $httpCode . " - Content-Type: " . $contentType);

        if ($httpCode != 200) {
            custom_log("[DEBUG] isValidImageUrl: Código de estado HTTP no es 200.");
            return false;
        }

        if ($contentType === null) {
            custom_log("[DEBUG] isValidImageUrl: Content-Type es nulo.");
            return false;
        }
        
        $isImage = (bool)preg_match('/^image\/(jpe?g|png|gif|webp|bmp|svg\+?xml)/i', $contentType);
        custom_log("[DEBUG] isValidImageUrl: Es imagen (" . ($isImage ? 'Sí' : 'No') . ").");
        
        return $isImage;
    }

    /**
     * Realiza una petición a la API de Telegram con manejo de reintentos y rate limiting
     * 
     * @param string $method Método de la API de Telegram
     * @param array $params Parámetros de la petición
     * @param int $retryCount Número de reintentos actual (para uso interno)
     * @param int $maxRetries Número máximo de reintentos
     * @return array Respuesta de la API de Telegram
     */
    private function apiRequest($method, $params, $retryCount = 0, $maxRetries = 3)
    {
        $url = $this->apiUrl . $this->botToken . '/' . $method;
        custom_log("[TELEGRAM API REQUEST] Method: {$method} | Params: " . json_encode($params));

        // Inicializar cURL
        $ch = curl_init($url);
        
        // Configurar opciones de cURL
        curl_setopt_array($ch, [
            CURLOPT_POST => 1,
            CURLOPT_POSTFIELDS => http_build_query($params),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_CONNECTTIMEOUT => 10, // 10 segundos para conectar
            CURLOPT_TIMEOUT => 30, // 30 segundos para la ejecución total
            CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded'],
        ]);
        
        // Ejecutar la petición
        $response_body = curl_exec($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);
        
        // Manejar errores de cURL
        if ($response_body === false) {
            custom_log("[TELEGRAM API ERROR] cURL Error: " . $error);
            
            // Si hay un error de conexión, reintentar con backoff exponencial
            if ($retryCount < $maxRetries) {
                $backoffTime = pow(2, $retryCount); // Backoff exponencial (2, 4, 8 segundos...)
                custom_log("Reintentando en {" . $backoffTime . "} segundos... (Intento " . ($retryCount + 1) . " de $maxRetries)");
                sleep($backoffTime);
                return $this->apiRequest($method, $params, $retryCount + 1, $maxRetries);
            }
            
            return ['ok' => false, 'description' => 'Error de conexión: ' . $error];
        }
        
        // Decodificar la respuesta
        $response_array = json_decode($response_body, true);
        
        // Si hay un error en la decodificación JSON
        if (json_last_error() !== JSON_ERROR_NONE) {
            custom_log("[TELEGRAM API ERROR] Error al decodificar JSON: " . json_last_error_msg());
            return ['ok' => false, 'description' => 'Error al decodificar la respuesta JSON'];
        }
        
        // Registrar la respuesta
        custom_log("[TELEGRAM API RESPONSE] HTTP Code: $http_code | Body: " . $response_body);
        
        // Manejar errores de rate limiting (código 429)
        if ($http_code === 429) {
            $retry_after = $response_array['parameters']['retry_after'] ?? 5; // Por defecto 5 segundos
            
            if ($retryCount < $maxRetries) {
                custom_log("Rate limit alcanzado. Reintentando en {" . $retry_after . "} segundos... (Intento " . ($retryCount + 1) . " de $maxRetries)");
                sleep($retry_after);
                return $this->apiRequest($method, $params, $retryCount + 1, $maxRetries);
            }
            
            custom_log("Se alcanzó el número máximo de reintentos para el método $method");
        }
        
        // Manejar otros códigos de error HTTP
        if ($http_code < 200 || $http_code >= 400) {
            $error_msg = $response_array['description'] ?? 'Error desconocido';
            custom_log("[TELEGRAM API ERROR] HTTP $http_code: $error_msg");
            
            // Si es un error 5xx, reintentar con backoff exponencial
            if ($http_code >= 500 && $retryCount < $maxRetries) {
                $backoffTime = pow(2, $retryCount);
                custom_log("Error del servidor. Reintentando en {" . $backoffTime . "} segundos... (Intento " . ($retryCount + 1) . " de $maxRetries)");
                sleep($backoffTime);
                return $this->apiRequest($method, $params, $retryCount + 1, $maxRetries);
            }
            
            return ['ok' => false, 'description' => "Error HTTP $http_code: $error_msg"];
        }
        
        return $response_array;
    }
}