<?php
/**
 * Telegram Bot Daemon
 * Ejecutar con Supervisor: php bot_telegram_daemon.php
 * Este demonio maneja interacciones en tiempo real y procesa mensajes programados y recurrentes.
 */

use TelegramBot\Api\BotApi;
use TelegramBot\Api\Types\Update;
use TelegramBot\Api\Types\Inline\InlineKeyboardMarkup;
use Carbon\Carbon;

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

// Cargar variables de entorno
if (file_exists(__DIR__ . '/.env')) {
    $lines = file(__DIR__ . '/.env', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    foreach ($lines as $line) {
        if (strpos(trim($line), '#') === 0) continue;
        if (strpos($line, '=') === false) continue;
        list($key, $value) = explode('=', $line, 2);
        $_ENV[trim($key)] = trim($value);
    }
}

// --- Funciones de Ayuda ---

/**
 * Obtiene una conexión a la base de datos.
 * @return PDO
 */
function getDBConnection() {
    $host = $_ENV['DB_HOST'];
    $db   = $_ENV['DB_NAME'];
    $user = $_ENV['DB_USER'];
    $pass = $_ENV['DB_PASS'];
    $port = $_ENV['DB_PORT'];
    $charset = 'utf8mb4';

    $dsn = "mysql:host=$host;port=$port;dbname=$db;charset=$charset";
    $options = [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES   => false,
    ];

    return new PDO($dsn, $user, $pass, $options);
}

/**
 * Procesa el contenido HTML para enviar a Telegram.
 * @param string $htmlContent
 * @return array ['cleanContent' => string, 'imageUrl' => ?string]
 */
function processContentForTelegram(string $htmlContent): array {
    $dom = new DOMDocument();
    libxml_use_internal_errors(true);
    $dom->loadHTML('<?xml encoding="utf-8" ?>' . $htmlContent, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
    libxml_clear_errors();
    
    $imageUrl = null;
    $images = $dom->getElementsByTagName('img');
    if ($images->length > 0) {
        $imageUrl = $images->item(0)->getAttribute('src');
    }
    
    // Limpiar texto: quitar tags HTML y decodificar entidades
    $cleanContent = strip_tags($htmlContent);
    $cleanContent = html_entity_decode($cleanContent, ENT_QUOTES, 'UTF-8');
    $cleanContent = trim($cleanContent);

    return ['cleanContent' => $cleanContent, 'imageUrl' => $imageUrl];
}


/**
 * Envía un mensaje a un chat de Telegram.
 * @param BotApi $bot
 * @param string $chatId
 * @param string $content
 * @param ?string $imageUrl
 * @return array|false Respuesta de la API de Telegram o false si falla.
 */
function sendMessageTelegram(BotApi $bot, string $chatId, string $content, ?string $imageUrl = null) {
    try {
        if ($imageUrl) {
            return $bot->sendPhoto($chatId, $imageUrl, $content);
        } else {
            return $bot->sendMessage($chatId, $content);
        }
    } catch (\Throwable $e) {
        error_log("Error enviando mensaje Telegram a $chatId: " . $e->getMessage());
        return false;
    }
}

/**
 * Calcula la próxima fecha de envío para un mensaje recurrente.
 * @param array $recurrence
 * @return Carbon
 */
function calculateNextSendTime(array $recurrence): Carbon {
    $tz = $_ENV['TIME_ZONE_ENVIOS'] ?? 'UTC';
    $time = $recurrence['hora_envio']; // HH:MM:SS

    // Iniciar con la hora actual en la zona horaria correcta y luego ajustar la hora del día
    $next = Carbon::now($tz)->setTimeFromTimeString($time);

    switch ($recurrence['frecuencia']) {
        case 'diario':
            // Si la hora ya pasó hoy, programar para mañana
            if ($next->isPast()) {
                $next->addDay();
            }
            break;
        case 'semanal':
            $dayOfWeekIso = (int)$recurrence['dia_semana']; // Lunes=1, ..., Domingo=7
            
            // Establecer el día de la semana
            $next->dayOfWeekIso($dayOfWeekIso);

            // Si la fecha/hora resultante ya pasó esta semana, ir a la semana que viene.
            if ($next->isPast()) {
                $next->addWeek();
            }
            break;
        case 'mensual':
            $dayOfMonth = (int)$recurrence['dia_mes'];
            
            // Si el día actual es mayor que el día programado, o si es el mismo día pero la hora ya pasó,
            // primero avanza al próximo mes para evitar errores.
            if (Carbon::now($tz)->day > $dayOfMonth || (Carbon::now($tz)->day == $dayOfMonth && $next->isPast())) {
                $next->addMonthNoOverflow();
            }
            
            // Establece el día del mes. Usar `day()` es más seguro en caso de meses con menos días.
            $next->day(min($dayOfMonth, $next->daysInMonth));

            break;
    }
    // Convertir a UTC para la base de datos
    return $next->setTimezone('UTC');
}

/**
 * Procesa los mensajes programados que están pendientes de envío para Telegram.
 * @param PDO $db
 * @param BotApi $bot
 */
function processScheduledMessagesTelegram(PDO $db, BotApi $bot) {
    echo "[Telegram Scheduler] Verificando mensajes programados..." . PHP_EOL;
    $stmt = $db->prepare("SELECT * FROM mensajes_telegram WHERE estado = 'pendiente' AND tipo_envio = 'programado' AND fecha_envio <= UTC_TIMESTAMP()");
    $stmt->execute();
    $messages = $stmt->fetchAll();

    foreach ($messages as $msg) {
        echo "[Telegram Scheduler] Procesando mensaje programado ID: {$msg['id']}" . PHP_EOL;
        $contentPayload = processContentForTelegram($msg['contenido']);

        $telegramResponse = sendMessageTelegram($bot, $msg['chat_id'], $contentPayload['cleanContent'], $contentPayload['imageUrl']);
        
        if ($telegramResponse) {
            $updateStmt = $db->prepare("UPDATE mensajes_telegram SET estado = 'enviado', mensaje_telegram_id = ? WHERE id = ?");
            $updateStmt->execute([$telegramResponse->getMessageId(), $msg['id']]);
            echo "[Telegram Scheduler] Mensaje programado ID: {$msg['id']} enviado." . PHP_EOL;
        } else {
            $updateStmt = $db->prepare("UPDATE mensajes_telegram SET estado = 'fallido' WHERE id = ?");
            $updateStmt->execute([$msg['id']]);
            echo "[Telegram Scheduler] Error al enviar mensaje programado ID: {$msg['id']}." . PHP_EOL;
        }
    }
}

/**
 * Procesa los mensajes recurrentes para Telegram.
 * @param PDO $db
 * @param BotApi $bot
 */
function processRecurringMessagesTelegram(PDO $db, BotApi $bot) {
    echo "[Telegram Scheduler] Verificando mensajes recurrentes..." . PHP_EOL;

    // 1. Inicializar `proximo_envio` para nuevos mensajes recurrentes
    $initStmt = $db->query("SELECT * FROM recurrentes_telegram WHERE proximo_envio IS NULL AND activo = 1");
    foreach ($initStmt->fetchAll() as $recurrence) {
        $nextSendTime = calculateNextSendTime($recurrence);
        $updateStmt = $db->prepare("UPDATE recurrentes_telegram SET proximo_envio = ? WHERE id = ?");
        $updateStmt->execute([$nextSendTime->toDateTimeString(), $recurrence['id']]);
        echo "[Telegram Scheduler] Inicializado proximo_envio para recurrente ID: {$recurrence['id']}" . PHP_EOL;
    }

    // 2. Procesar mensajes recurrentes listos para ser enviados
    $stmt = $db->prepare("
        SELECT r.*, m.contenido, m.chat_id, m.usuario_id 
        FROM recurrentes_telegram r
        JOIN mensajes_telegram m ON r.mensaje_id = m.id
        WHERE r.activo = 1 AND r.proximo_envio <= UTC_TIMESTAMP()
    ");
    $stmt->execute();
    $recurrences = $stmt->fetchAll();

    foreach ($recurrences as $rec) {
        echo "[Telegram Scheduler] Procesando mensaje recurrente ID: {$rec['id']}" . PHP_EOL;
        $contentPayload = processContentForTelegram($rec['contenido']);

        $telegramResponse = sendMessageTelegram($bot, $rec['chat_id'], $contentPayload['cleanContent'], $contentPayload['imageUrl']);
        
        if ($telegramResponse) {
            // Registrar este envío específico en el historial
            $logStmt = $db->prepare("
                INSERT INTO mensajes_telegram (usuario_id, chat_id, contenido, estado, mensaje_telegram_id, fecha_envio, tipo_envio)
                VALUES (?, ?, ?, 'enviado', ?, NOW(), 'recurrente_enviado')
            ");
            $logStmt->execute([$rec['usuario_id'], $rec['chat_id'], $rec['contenido'], $telegramResponse->getMessageId()]);
            
            // Calcular y actualizar el próximo envío
            $nextSendTime = calculateNextSendTime($rec);
            $updateStmt = $db->prepare("UPDATE recurrentes_telegram SET proximo_envio = ? WHERE id = ?");
            $updateStmt->execute([$nextSendTime->toDateTimeString(), $rec['id']]);
            
            echo "[Telegram Scheduler] Mensaje recurrente ID: {$rec['id']} enviado. Próximo envío: " . $nextSendTime->toDateTimeString() . PHP_EOL;
        } else {
            error_log("[Telegram Scheduler] Error al enviar mensaje recurrente ID: {$rec['id']}.");
            // No se actualiza el proximo_envio para reintentar en la próxima ejecución si falló
        }
    }
}

/**
 * Registra o actualiza un destinatario de Telegram en la base de datos.
 * @param PDO $db
 * @param string $id
 * @param string $type
 * @param ?string $name
 * @param ?string $username
 */
function registerTelegramRecipient(PDO $db, $id, $type, $name, $username = null) {
    if (empty($id)) return;

    $name = $name ?: ($username ?: "Destinatario {$id}");

    $stmt = $db->prepare("SELECT id FROM destinatarios_telegram WHERE telegram_id = ?");
    $stmt->execute([$id]);
    
    if ($stmt->fetch()) {
        // Actualizar
        $updateStmt = $db->prepare(
            "UPDATE destinatarios_telegram SET nombre = ?, username = ?, ultima_interaccion = NOW() WHERE telegram_id = ?"
        );
        $updateStmt->execute([$name, $username, $id]);
    } else {
        // Insertar
        $insertStmt = $db->prepare(
            "INSERT INTO destinatarios_telegram (telegram_id, tipo, nombre, username, fecha_registro, ultima_interaccion) VALUES (?, ?, ?, ?, NOW(), NOW())"
        );
        $insertStmt->execute([$id, $type, $name, $username]);
        echo "[Recipient Registrar] Nuevo destinatario: {$name} ({$id}) de tipo {$type}" . PHP_EOL;
    }
}


// --- Inicio del Bot de Polling ---

$bot = new BotApi($_ENV['TELEGRAM_BOT_TOKEN']);

echo "Telegram Bot iniciado", PHP_EOL;

$offset = 0;
while (true) {
    try {
        $updates = $bot->getUpdates($offset, 100, 1);
        
        $db = getDBConnection(); // Obtener conexión a DB en cada iteración del bucle (o manejar persistencia)

        // --- Procesar mensajes programados y recurrentes ---
        processScheduledMessagesTelegram($db, $bot);
        processRecurringMessagesTelegram($db, $bot);


        foreach ($updates as $update) {
            $offset = $update->getUpdateId() + 1;
            
            // --- Registrar/Actualizar Destinatarios ---
            $message = $update->getMessage();
            $callbackQuery = $update->getCallbackQuery();

            $chat = null;
            $user = null;

            if ($message) {
                $chat = $message->getChat();
                $user = $message->getFrom();
            } elseif ($callbackQuery) {
                $chat = $callbackQuery->getMessage()->getChat();
                $user = $callbackQuery->getFrom();
            }

            if ($chat) {
                registerTelegramRecipient(
                    $db,
                    $chat->getId(),
                    $chat->getType(),
                    $chat->getTitle() ?: $chat->getFirstName(),
                    $chat->getUsername()
                );
            }

            if ($user && (!$chat || $user->getId() != $chat->getId())) {
                registerTelegramRecipient(
                    $db,
                    $user->getId(),
                    'usuario',
                    $user->getFirstName(),
                    $user->getUsername()
                );
            }

            // Manejar comandos de texto
            if ($message && !empty($message->getText())) {
                $text = $message->getText();
                if (strpos($text, '/') === 0) { // Es un comando
                    $command = explode(' ', $text)[0];
                    $chatId = $message->getChat()->getId();
                    $user = $message->getFrom();

                    switch ($command) {
                        case '/start':
                            // Reutilizar la lógica de bienvenida para el comando /start
                            sendWelcomeMessage($bot, $chatId, $user);
                            break;
                        case '/help':
                            $responseText = "Comandos disponibles:\n/start - Muestra el mensaje de bienvenida.\n/help - Muestra esta ayuda.";
                            sendMessageTelegram($bot, $chatId, $responseText);
                            break;
                        default:
                            // Comprobar si es un comando de plantilla
                            $cmdName = ltrim($command, '/');
                            $stmt = $db->prepare("SELECT contenido FROM plantillas_telegram WHERE comando = ?");
                            $stmt->execute([$cmdName]);
                            $plantilla = $stmt->fetch();
                            if ($plantilla) {
                                $contentPayload = processContentForTelegram($plantilla['contenido']);
                                sendMessageTelegram($bot, $chatId, $contentPayload['cleanContent'], $contentPayload['imageUrl']);
                            } else {
                                sendMessageTelegram($bot, $chatId, "Comando no reconocido.");
                            }
                            break;
                    }
                }
            }

            // Manejar callback queries (botones de idioma)
            if ($callbackQuery) {
                $userId = $callbackQuery->getFrom()->getId();
                $username = $callbackQuery->getFrom()->getUsername() ?? $callbackQuery->getFrom()->getFirstName();
                $data = $callbackQuery->getData();
                
                echo "Callback recibido de $username: $data", PHP_EOL;
                
                if (strpos($data, 'lang_select_') === 0) {
                    $langCode = substr($data, strlen('lang_select_'));
                    
                    try {
                        // La lógica para guardar el idioma ya está cubierta por registerTelegramRecipient,
                        // pero aquí actualizamos específicamente 'idioma_detectado'.
                        $stmt = $db->prepare("UPDATE destinatarios_telegram SET idioma_detectado = ? WHERE telegram_id = ?");
                        $stmt->execute([$langCode, $userId]);
                        
                        echo "Preferencia de idioma guardada en BD para $username.", PHP_EOL;
                        
                        $bot->answerCallbackQuery($callbackQuery->getId(), "✅ Idioma seleccionado: $langCode", true);
                        
                    } catch (\Exception $e) {
                        echo "Error guardando preferencia de idioma: " . $e->getMessage(), PHP_EOL;
                        $bot->answerCallbackQuery($callbackQuery->getId(), "Error guardando preferencia", true);
                    }
                }
            }
            
            // Manejar nuevos miembros (bienvenida)
            if ($message && $newMembers = $message->getNewChatMembers()) {
                foreach ($newMembers as $member) {
                    sendWelcomeMessage($bot, $message->getChat()->getId(), $member);
                }
            }
        }
        
        usleep(500000); // 0.5 segundos de pausa para no saturar la API
        
    } catch (\Exception $e) {
        echo "Error en polling: " . $e->getMessage(), PHP_EOL;
        
        // Log error
        try {
            $db = getDBConnection();
            $stmt = $db->prepare("INSERT INTO logs_telegram (origen, nivel, descripcion, datos_json) VALUES ('bot', 'error', ?, ?)");
            $stmt->execute([
                "Error en polling: " . $e->getMessage(),
                json_encode(['trace' => $e->getTraceAsString()])
            ]);
        } catch (\Exception $ex) {}
        
        sleep(5); // Esperar un poco más en caso de error
    }
}

function sendWelcomeMessage($bot, $chatId, $user) {
    try {
        $db = getDBConnection(); // Obtener conexión a DB
        
        // Obtener configuración de bienvenida
        $stmt = $db->query("SELECT * FROM bienvenida_telegram WHERE activo = 1 LIMIT 1");
        $config = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if (!$config) return;
        
        // Obtener idiomas activos
        $stmt = $db->query("SELECT codigo, nombre, nombre_nativo, bandera FROM idiomas WHERE activo = 1 ORDER BY nombre ASC");
        $idiomas = $stmt->fetchAll(PDO::FETCH_ASSOC);
        
        // Preparar mensaje
        $username = $user->getUsername() ? '@' . $user->getUsername() : $user->getFirstName();
        $texto = str_replace('{usuario}', $username, $config['texto']);
        
        // Limpiar HTML simple
        $texto = strip_tags($texto);
        
        // Crear botones de idioma
        $keyboard = [];
        $currentRow = [];
        foreach ($idiomas as $lang) {
            if (count($currentRow) >= 3) {
                $keyboard[] = $currentRow;
                $currentRow = [];
            }
            
            $label = $lang['bandera'] ?: $lang['nombre'];
            $currentRow[] = [
                'text' => $label,
                'callback_data' => 'lang_select_' . $lang['codigo']
            ];
        }
        if (!empty($currentRow)) {
            $keyboard[] = $currentRow;
        }
        
        // Crear el objeto de markup para los botones
        $replyMarkup = new InlineKeyboardMarkup($keyboard);

        // Enviar mensaje
        $bot->sendMessage(
            $chatId,
            $texto,
            'HTML', // Usar HTML parse mode para el mensaje de bienvenida
            false,
            null,
            $replyMarkup
        );
        
        echo "Mensaje de bienvenida enviado a $username", PHP_EOL;
        
    } catch (\Exception $e) {
        echo "Error enviando bienvenida: " . $e->getMessage(), PHP_EOL;
    }
}
