'Método no permitido']); exit; } try { $input = json_decode(file_get_contents('php://input'), true); if (!$input) { http_response_code(400); echo json_encode(['error' => 'Datos requeridos']); exit; } $selectionType = $input['selection_type'] ?? 'filter'; $messageType = $input['message_type'] ?? 'text'; $db = Database::getInstance(); $whatsappService = new WhatsAppService(); // Obtener usuarios según tipo de selección $users = []; if ($selectionType === 'specific') { // Usuarios específicos seleccionados if (!isset($input['user_ids']) || !is_array($input['user_ids']) || empty($input['user_ids'])) { http_response_code(400); echo json_encode(['error' => 'Debes seleccionar al menos un usuario']); exit; } $userIds = array_map('intval', $input['user_ids']); $placeholders = implode(',', array_fill(0, count($userIds), '?')); $query = "SELECT id, phone_number, name FROM users WHERE id IN ($placeholders) ORDER BY id ASC"; $users = $db->fetchAll($query, $userIds); } else { // Por filtro $filter = $input['filter'] ?? 'all'; $whereClause = "1=1"; switch ($filter) { case 'active': $whereClause .= " AND EXISTS ( SELECT 1 FROM conversations c WHERE c.user_id = u.id AND c.created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) )"; break; case 'recent': $whereClause .= " AND u.created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)"; break; case 'all': // Incluir todos los usuarios break; } $query = "SELECT id, phone_number, name FROM users u WHERE " . $whereClause . " ORDER BY id ASC"; $users = $db->fetchAll($query); } if (empty($users)) { http_response_code(404); echo json_encode([ 'error' => 'No se encontraron usuarios para enviar', 'selection_type' => $selectionType ]); exit; } $sentCount = 0; $errorCount = 0; $errors = []; foreach ($users as $user) { try { $response = null; $messageContent = ''; // Enviar según tipo de mensaje if ($messageType === 'template') { // Enviar plantilla if (!isset($input['template_id'])) { throw new Exception('ID de plantilla requerido'); } $templateId = intval($input['template_id']); $template = $db->fetch("SELECT * FROM message_templates WHERE id = ?", [$templateId]); if (!$template) { throw new Exception('Plantilla no encontrada'); } $status = strtoupper($template['status']); if ($status !== 'APPROVED') { throw new Exception('La plantilla no está aprobada. Status: ' . $template['status']); } // Preparar variables $templateVariables = $input['template_variables'] ?? []; error_log('📝 Variables recibidas: ' . json_encode($templateVariables)); error_log('🔍 Tipo: ' . gettype($templateVariables)); // Detectar si son variables numéricas o con nombres $isNumericVars = false; $isNamedVars = false; if (is_array($templateVariables) && !empty($templateVariables)) { $firstKey = array_key_first($templateVariables); if (is_int($firstKey) && isset($templateVariables[0])) { // Array indexado numéricamente [0 => "val1", 1 => "val2"] $isNumericVars = true; error_log('✅ Variables tipo: Array numérico indexado'); } elseif (is_string($firstKey) && !is_numeric($firstKey)) { // Array asociativo con nombres ["nombre_tema" => "val1", "fecha" => "val2"] $isNamedVars = true; error_log('✅ Variables tipo: Objeto con nombres (asociativo)'); } elseif (is_numeric($firstKey)) { // Array con claves numéricas [1 => "val1", 2 => "val2"] $isNumericVars = true; error_log('✅ Variables tipo: Array con claves numéricas'); } } // Procesar según el tipo $processedVariables = []; if ($isNamedVars) { // Variables con nombres: pasar como objeto asociativo $processedVariables = $templateVariables; error_log('📦 Enviando como objeto con nombres: ' . json_encode($processedVariables)); } elseif ($isNumericVars) { // Variables numéricas: ordenar y convertir a array indexado ksort($templateVariables, SORT_NUMERIC); foreach ($templateVariables as $value) { $processedVariables[] = $value; } error_log('📊 Enviando como array ordenado: ' . json_encode($processedVariables)); } else { error_log('⚠️ Sin variables o formato desconocido'); } error_log('📤 Variables procesadas: ' . json_encode($processedVariables)); // Verificar variables si la plantilla usa formato numérico $expectedVars = preg_match_all('/\{\{(\d+)\}\}/', $template['body_text'] ?? '', $matches); if ($expectedVars > 0 && $isNumericVars && count($processedVariables) !== $expectedVars) { throw new Exception("La plantilla requiere {$expectedVars} variable(s) numéricas, pero se enviaron " . count($processedVariables)); } $response = $whatsappService->sendTemplateMessage( $user['phone_number'], $template['template_name'] ?? $template['name'], $template['language_code'] ?? 'es', $processedVariables ); // Construir contenido para BD (reemplazar variables en body_text) $messageContent = $template['body_text'] ?? $template['name']; if ($isNamedVars) { // Reemplazar por nombres foreach ($templateVariables as $key => $value) { $messageContent = str_replace('{{' . $key . '}}', $value, $messageContent); } } else { // Reemplazar por índice numérico foreach ($processedVariables as $index => $value) { $messageContent = str_replace('{{' . ($index + 1) . '}}', $value, $messageContent); } } } else { // Enviar texto simple $message = trim($input['message'] ?? ''); if (empty($message)) { throw new Exception('Mensaje vacío'); } $response = $whatsappService->sendTextMessage($user['phone_number'], $message); $messageContent = $message; } if ($response && isset($response['success']) && $response['success']) { $sentCount++; // Guardar mensaje en BD $db->insert('conversations', [ 'user_id' => $user['id'], 'message_id' => $response['message_id'] ?? null, 'content' => $messageContent, 'direction' => 'outgoing', 'message_type' => $messageType === 'template' ? 'template' : 'text', 'status' => 'sent', 'created_at' => date('Y-m-d H:i:s') ]); } else { $errorCount++; $errors[] = [ 'phone' => $user['phone_number'], 'error' => 'Respuesta inválida de WhatsApp' ]; } // Pausa para no saturar la API (250ms entre mensajes) usleep(250000); } catch (Exception $e) { $errorCount++; $errors[] = [ 'phone' => $user['phone_number'], 'name' => $user['name'] ?? 'Sin nombre', 'error' => $e->getMessage() ]; error_log("Error sending broadcast to {$user['phone_number']}: " . $e->getMessage()); } } echo json_encode([ 'success' => true, 'sent_count' => $sentCount, 'error_count' => $errorCount, 'total_users' => count($users), 'selection_type' => $selectionType, 'message_type' => $messageType, 'errors' => $errorCount > 0 ? $errors : null ]); } catch (Exception $e) { error_log("Error in send_broadcast.php: " . $e->getMessage()); http_response_code(500); echo json_encode(['error' => 'Error interno del servidor: ' . $e->getMessage()]); } ?>