/**
 * Copyright (c) 2024-2025 Тарабанов Александр Викторович
 * All rights reserved.
 * 
 * This software is proprietary and confidential.
 * Unauthorized copying, modification, or distribution is prohibited.
 * 
 * For licensing inquiries: info@hb3-accelerator.com
 * Website: https://hb3-accelerator.com
 * GitHub: https://github.com/HB3-ACCELERATOR
 */

const { Telegraf } = require('telegraf');
const logger = require('../utils/logger');
const encryptedDb = require('./encryptedDatabaseService');
const db = require('../db');
const authService = require('./auth-service');
const verificationService = require('./verification-service');
const crypto = require('crypto');
const identityService = require('./identity-service');
const aiAssistant = require('./ai-assistant');
const { checkAdminRole } = require('./admin-role');
const { broadcastContactsUpdate, broadcastChatMessage } = require('../wsHub');
const aiAssistantSettingsService = require('./aiAssistantSettingsService');
const { ragAnswer, generateLLMResponse } = require('./ragService');
const { isUserBlocked } = require('../utils/userUtils');

let botInstance = null;
let telegramSettingsCache = null;

async function getTelegramSettings() {
  if (telegramSettingsCache) return telegramSettingsCache;
  
  const settings = await encryptedDb.getData('telegram_settings', {}, 1);
  if (!settings.length) throw new Error('Telegram settings not found in DB');
  
  telegramSettingsCache = settings[0];
  return telegramSettingsCache;
}

// Создание и настройка бота
async function getBot() {
      // console.log('[TelegramBot] getBot() called');
  if (!botInstance) {
          // console.log('[TelegramBot] Creating new bot instance...');
    const settings = await getTelegramSettings();
          // console.log('[TelegramBot] Got settings, creating Telegraf instance...');
    botInstance = new Telegraf(settings.bot_token);
          // console.log('[TelegramBot] Telegraf instance created');

    // Обработка команды /start
    botInstance.command('start', (ctx) => {
      ctx.reply('Добро пожаловать! Отправьте код подтверждения для аутентификации.');
    });

    // Универсальный обработчик текстовых сообщений
    botInstance.on('text', async (ctx) => {
      const text = ctx.message.text.trim();
      // 1. Если команда — пропустить
      if (text.startsWith('/')) return;
      
      // Отправляем индикатор печати для улучшения UX
      const typingAction = ctx.replyWithChatAction('typing');
      
      // 2. Проверка: это потенциальный код?
      const isPotentialCode = (str) => /^[A-Z0-9]{6}$/i.test(str);
      if (isPotentialCode(text)) {
        await typingAction;
        try {
          // Получаем код верификации для всех активных кодов с провайдером telegram
          const codes = await encryptedDb.getData('verification_codes', {
            code: text.toUpperCase(),
            provider: 'telegram',
            used: false
          }, 1);

          if (codes.length === 0) {
            ctx.reply('Неверный код подтверждения');
            return;
          }

          const verification = codes[0];
          const providerId = verification.provider_id;
          const linkedUserId = verification.user_id; // Получаем связанный userId если он есть
          let userId;
          let userRole = 'user'; // Роль по умолчанию

          // Отмечаем код как использованный
          await encryptedDb.saveData('verification_codes', {
            used: true
          }, {
            id: verification.id
          });

          logger.info('Starting Telegram auth process for code:', text);

          // Проверяем, существует ли уже пользователь с таким Telegram ID
          const existingTelegramUsers = await encryptedDb.getData('user_identities', {
            provider: 'telegram',
            provider_id: ctx.from.id.toString()
          }, 1);

          if (existingTelegramUsers.length > 0) {
            // Если пользователь с таким Telegram ID уже существует, используем его
            userId = existingTelegramUsers[0].user_id;
            logger.info(`Using existing user ${userId} for Telegram account ${ctx.from.id}`);
          } else {
            // Если код верификации был связан с существующим пользователем, используем его
            if (linkedUserId) {
              // Используем userId из кода верификации
              userId = linkedUserId;
              // Связываем Telegram с этим пользователем
              await encryptedDb.saveData('user_identities', {
                user_id: userId,
                provider: 'telegram',
                provider_id: ctx.from.id.toString()
              });
              logger.info(
                `Linked Telegram account ${ctx.from.id} to pre-authenticated user ${userId}`
              );
            } else {
              // Проверяем, есть ли пользователь, связанный с гостевым идентификатором
              let existingUserWithGuestId = null;
              if (providerId) {
                const guestUserResult = await encryptedDb.getData('guest_user_mapping', {
                  guest_id: providerId
                }, 1);
                if (guestUserResult.length > 0) {
                  existingUserWithGuestId = guestUserResult[0].user_id;
                  logger.info(
                    `Found existing user ${existingUserWithGuestId} by guest ID ${providerId}`
                  );
                }
              }

              if (existingUserWithGuestId) {
                // Используем существующего пользователя и добавляем ему Telegram идентификатор
                userId = existingUserWithGuestId;
                await encryptedDb.saveData('user_identities', {
                  user_id: userId,
                  provider: 'telegram',
                  provider_id: ctx.from.id.toString()
                });
                logger.info(`Linked Telegram account ${ctx.from.id} to existing user ${userId}`);
              } else {
                // Создаем нового пользователя, если не нашли существующего
                const userResult = await encryptedDb.saveData('users', {
                  created_at: new Date(),
                  role: 'user'
                });
                userId = userResult.id;

                // Связываем Telegram с новым пользователем
                await encryptedDb.saveData('user_identities', {
                  user_id: userId,
                  provider: 'telegram',
                  provider_id: ctx.from.id.toString()
                });

                // Если был гостевой ID, связываем его с новым пользователем
                if (providerId) {
                  await encryptedDb.saveData('guest_user_mapping', {
                    user_id: userId,
                    guest_id: providerId
                  }, {
                    user_id: userId
                  });
                }

                logger.info(`Created new user ${userId} with Telegram account ${ctx.from.id}`);
              }
            }
          }

          // ----> НАЧАЛО: Проверка роли на основе привязанного кошелька <----
          if (userId) { // Убедимся, что userId определен
            logger.info(`[TelegramBot] Checking linked wallet for determined userId: ${userId} (Type: ${typeof userId})`);
            try {
              const linkedWallet = await authService.getLinkedWallet(userId);
              if (linkedWallet) { 
                logger.info(`[TelegramBot] Found linked wallet ${linkedWallet} for user ${userId}. Checking role...`);
                const isAdmin = await checkAdminRole(linkedWallet);
                userRole = isAdmin ? 'admin' : 'user';
                logger.info(`[TelegramBot] Role for user ${userId} determined as: ${userRole}`);

                // Опционально: Обновить роль в таблице users
                const currentUser = await encryptedDb.getData('users', {
                  id: userId
                }, 1);
                if (currentUser.length > 0 && currentUser[0].role !== userRole) {
                  await encryptedDb.saveData('users', {
                    role: userRole
                  }, {
                    id: userId
                  });
                  logger.info(`[TelegramBot] Updated user role in DB to ${userRole}`);
                }
              } else {
                logger.info(`[TelegramBot] No linked wallet found for user ${userId}. Checking current DB role.`);
                // Если кошелька нет, берем текущую роль из базы
                const currentUser = await encryptedDb.getData('users', {
                  id: userId
                }, 1);
                if (currentUser.length > 0) {
                  userRole = currentUser[0].role;
                }
              }
            } catch (roleCheckError) {
              logger.error(`[TelegramBot] Error checking admin role for user ${userId}:`, roleCheckError);
              // В случае ошибки берем роль из базы или оставляем 'user'
              try {
                  const currentUser = await encryptedDb.getData('users', {
                    id: userId
                  }, 1);
                  if (currentUser.length > 0) { userRole = currentUser[0].role; }
              } catch (dbError) { /* ignore */ }
            }
          } else {
             logger.error('[TelegramBot] Cannot check role because userId is undefined!');
          }
          // ----> КОНЕЦ: Проверка роли <----

          // Логируем userId перед обновлением сессии
          logger.info(`[telegramBot] Attempting to update session for userId: ${userId}`);

          // Находим последнюю активную сессию для данного userId
          let activeSessionId = null;
          try {
            // Ищем сессию, где есть userId и она не истекла (проверка expires_at)
            // Сортируем по expires_at DESC чтобы взять самую "свежую", если их несколько
            const sessionResult = await encryptedDb.getData('session', {
              'sess->>userId': userId?.toString()
            }, 1, 'expire', 'DESC');
            
            if (sessionResult.length > 0) {
              activeSessionId = sessionResult[0].sid;
              logger.info(`[telegramBot] Found active session ID ${activeSessionId} for user ${userId}`);

              // Обновляем найденную сессию в базе данных, добавляя/перезаписывая данные Telegram
              const updateResult = await encryptedDb.saveData('session', {
                sess: JSON.stringify({
                  // authenticated: true, // Не перезаписываем, т.к. сессия уже должна быть аутентифицирована
                  authType: 'telegram', // Обновляем тип аутентификации
                  telegramId: ctx.from.id.toString(),
                  telegramUsername: ctx.from.username,
                  telegramFirstName: ctx.from.first_name,
                  role: userRole, // Записываем определенную роль
                  // userId: userId?.toString() // userId уже должен быть в сессии
                })
              }, {
                sid: activeSessionId
              });
              
              if (updateResult.rowCount > 0) {
                  logger.info(`[telegramBot] Session ${activeSessionId} updated successfully with Telegram data for user ${userId}`);
              } else {
                  logger.warn(`[telegramBot] Session update query executed but did not update rows for sid: ${activeSessionId}. This might indicate a concurrency issue or incorrect sid.`);
              }

            } else {
              logger.warn(`[telegramBot] No active web session found for userId: ${userId}. Telegram is linked, but the user might need to refresh their browser session.`);
            }
          } catch(sessionError) {
             logger.error(`[telegramBot] Error finding or updating session for userId ${userId}:`, sessionError);
          }

          // Отправляем сообщение об успешной аутентификации
          await ctx.reply('Аутентификация успешна! Можете вернуться в приложение.');

          // Удаляем сообщение с кодом
          try {
            await ctx.deleteMessage(ctx.message.message_id);
          } catch (error) {
            logger.warn('Could not delete code message:', error);
          }

          // После каждого успешного создания пользователя:
          broadcastContactsUpdate();
        } catch (error) {
          logger.error('Error in Telegram auth:', error);
          await ctx.reply('Произошла ошибка при аутентификации. Попробуйте позже.');
        }
        return;
      }
      
            // 3. Всё остальное — чат с ИИ-ассистентом
      try {
        const telegramId = ctx.from.id.toString();
        
        // 1. Найти или создать пользователя
        const { userId, role } = await identityService.findOrCreateUserWithRole('telegram', telegramId);
        if (await isUserBlocked(userId)) {
          await ctx.reply('Вы заблокированы. Сообщения не принимаются.');
          return;
        }
        
        // 1.1 Найти или создать беседу
        let conversationResult = await encryptedDb.getData('conversations', {
          user_id: userId
        }, 1, 'updated_at', 'DESC', 'created_at', 'DESC');
        let conversation;
        if (conversationResult.length === 0) {
          const title = `Чат с пользователем ${userId}`;
          const newConv = await encryptedDb.saveData('conversations', {
            user_id: userId,
            title: title,
            created_at: new Date(),
            updated_at: new Date()
          });
          conversation = newConv;
        } else {
          conversation = conversationResult[0];
        }
        
        // 2. Сохранять все сообщения с conversation_id
        let content = text;
        let attachmentMeta = {};
        // Проверяем вложения (фото, документ, аудио, видео)
        let fileId, fileName, mimeType, fileSize, attachmentBuffer;
        if (ctx.message.document) {
          fileId = ctx.message.document.file_id;
          fileName = ctx.message.document.file_name;
          mimeType = ctx.message.document.mime_type;
          fileSize = ctx.message.document.file_size;
        } else if (ctx.message.photo && ctx.message.photo.length > 0) {
          // Берём самое большое фото
          const photo = ctx.message.photo[ctx.message.photo.length - 1];
          fileId = photo.file_id;
          fileName = 'photo.jpg';
          mimeType = 'image/jpeg';
          fileSize = photo.file_size;
        } else if (ctx.message.audio) {
          fileId = ctx.message.audio.file_id;
          fileName = ctx.message.audio.file_name || 'audio.ogg';
          mimeType = ctx.message.audio.mime_type || 'audio/ogg';
          fileSize = ctx.message.audio.file_size;
        } else if (ctx.message.video) {
          fileId = ctx.message.video.file_id;
          fileName = ctx.message.video.file_name || 'video.mp4';
          mimeType = ctx.message.video.mime_type || 'video/mp4';
          fileSize = ctx.message.video.file_size;
        }
        
        // Асинхронная загрузка файлов
        if (fileId) {
          try {
            const fileLink = await ctx.telegram.getFileLink(fileId);
            const res = await fetch(fileLink.href);
            attachmentBuffer = await res.buffer();
            attachmentMeta = {
              attachment_filename: fileName,
              attachment_mimetype: mimeType,
              attachment_size: fileSize,
              attachment_data: attachmentBuffer
            };
          } catch (fileError) {
            logger.error('[TelegramBot] Error downloading file:', fileError);
            // Продолжаем без файла
          }
        }
        
        // Сохраняем сообщение в БД
        if (!conversation || !conversation.id) {
          logger.error(`[TelegramBot] Conversation is undefined or has no id for user ${userId}`);
          await ctx.reply('Произошла ошибка при создании диалога. Попробуйте позже.');
          return;
        }
        
        const userMessage = await encryptedDb.saveData('messages', {
          user_id: userId,
          conversation_id: conversation.id,
          sender_type: 'user',
          content: content,
          channel: 'telegram',
          role: role,
          direction: 'in',
          created_at: new Date(),
          attachment_filename: attachmentMeta.attachment_filename || null,
          attachment_mimetype: attachmentMeta.attachment_mimetype || null,
          attachment_size: attachmentMeta.attachment_size || null,
          attachment_data: attachmentMeta.attachment_data || null
        });
        
        // Отправляем WebSocket уведомление о пользовательском сообщении
        try {
          const decryptedUserMessage = await encryptedDb.getData('messages', { id: userMessage.id }, 1);
          if (decryptedUserMessage && decryptedUserMessage[0]) {
            broadcastChatMessage(decryptedUserMessage[0], userId);
          }
        } catch (wsError) {
          logger.error('[TelegramBot] WebSocket notification error for user message:', wsError);
        }

        if (await isUserBlocked(userId)) {
          logger.info(`[TelegramBot] Пользователь ${userId} заблокирован — ответ ИИ не отправляется.`);
          return;
        }

        // 3. Получить ответ от ИИ (RAG + LLM) - асинхронно
        const aiResponsePromise = (async () => {
          const aiSettings = await aiAssistantSettingsService.getSettings();
          let ragTableId = null;
          if (aiSettings && aiSettings.selected_rag_tables) {
            ragTableId = Array.isArray(aiSettings.selected_rag_tables)
              ? aiSettings.selected_rag_tables[0]
              : aiSettings.selected_rag_tables;
          }
          
          // Загружаем историю сообщений для контекста (ограничиваем до 5 сообщений)
          let history = null;
          try {
            const recentMessages = await encryptedDb.getData('messages', {
              conversation_id: conversation.id
            }, 5, 'created_at DESC');
            
            if (recentMessages && recentMessages.length > 0) {
              // Преобразуем сообщения в формат для AI
              history = recentMessages.reverse().map(msg => ({
                role: msg.sender_type === 'user' ? 'user' : 'assistant',
                content: msg.content || '' // content уже расшифрован encryptedDb
              }));
            }
          } catch (historyError) {
            logger.error('[TelegramBot] Error loading message history:', historyError);
          }
          
          let aiResponse;
          if (ragTableId) {
            // Сначала ищем ответ через RAG
            const ragResult = await ragAnswer({ tableId: ragTableId, userQuestion: content });
            if (ragResult && ragResult.answer && typeof ragResult.score === 'number' && Math.abs(ragResult.score) <= 0.3) {
              aiResponse = ragResult.answer;
            } else {
              aiResponse = await generateLLMResponse({
                userQuestion: content,
                context: ragResult && ragResult.context ? ragResult.context : '',
                answer: ragResult && ragResult.answer ? ragResult.answer : '',
                systemPrompt: aiSettings ? aiSettings.system_prompt : '',
                history: history,
                model: aiSettings ? aiSettings.model : undefined,
                language: aiSettings && aiSettings.languages && aiSettings.languages.length > 0 ? aiSettings.languages[0] : 'ru'
              });
            }
          } else {
            // Используем системный промпт из настроек, если RAG не используется
            const systemPrompt = aiSettings ? aiSettings.system_prompt : '';
            aiResponse = await aiAssistant.getResponse(content, 'auto', history, systemPrompt);
          }
          
          return aiResponse;
        })();
        
        // Ждем ответ от ИИ с таймаутом
        const aiResponse = await Promise.race([
          aiResponsePromise,
                  new Promise((_, reject) => 
          setTimeout(() => reject(new Error('AI response timeout')), 120000)
        )
        ]);
        
        // 4. Сохранить ответ в БД с conversation_id
        const aiMessage = await encryptedDb.saveData('messages', {
          user_id: userId,
          conversation_id: conversation.id,
          sender_type: 'assistant',
          content: aiResponse,
          channel: 'telegram',
          role: role,
          direction: 'out',
          created_at: new Date()
        });
        
        // 5. Отправить ответ пользователю
        await ctx.reply(aiResponse);
        
        // 6. Отправить WebSocket уведомление
        try {
          const decryptedAiMessage = await encryptedDb.getData('messages', { id: aiMessage.id }, 1);
          if (decryptedAiMessage && decryptedAiMessage[0]) {
            broadcastChatMessage(decryptedAiMessage[0], userId);
          }
        } catch (wsError) {
          logger.error('[TelegramBot] WebSocket notification error:', wsError);
        }
      } catch (error) {
        logger.error('[TelegramBot] Ошибка при обработке сообщения:', error);
        await ctx.reply('Произошла ошибка при обработке вашего сообщения. Попробуйте позже.');
      }
    });

    // Запуск бота с таймаутом
    // console.log('[TelegramBot] Before botInstance.launch()');
    try {
      // Запускаем бота с таймаутом
      const launchPromise = botInstance.launch();
      const timeoutPromise = new Promise((_, reject) => {
        setTimeout(() => reject(new Error('Telegram bot launch timeout')), 30000); // 30 секунд таймаут
      });
      
      await Promise.race([launchPromise, timeoutPromise]);
      // console.log('[TelegramBot] After botInstance.launch()');
      logger.info('[TelegramBot] Бот запущен');
    } catch (error) {
              // console.error('[TelegramBot] Error launching bot:', error);
      // Не выбрасываем ошибку, чтобы не блокировать запуск сервера
      // console.log('[TelegramBot] Bot launch failed, but continuing...');
    }
  }

  return botInstance;
}

// Остановка бота
async function stopBot() {
  if (botInstance) {
    try {
      await botInstance.stop();
      botInstance = null;
      logger.info('Telegram bot stopped successfully');
    } catch (error) {
      logger.error('Error stopping Telegram bot:', error);
      throw error;
    }
  }
}

// Инициализация процесса аутентификации
async function initTelegramAuth(session) {
  try {
    // Используем временный идентификатор для создания кода верификации
    // Реальный пользователь будет создан или найден при проверке кода через бота
    const tempId = crypto.randomBytes(16).toString('hex');

    // Если пользователь уже авторизован, сохраняем его userId в guest_user_mapping
    // чтобы потом при авторизации через бота этот пользователь был найден
    if (session && session.authenticated && session.userId) {
      const guestId = session.guestId || tempId;

      // Связываем гостевой ID с текущим пользователем
      await encryptedDb.saveData('guest_user_mapping', {
        user_id: session.userId,
        guest_id: guestId
      }, {
        user_id: session.userId
      });

      logger.info(
        `[initTelegramAuth] Linked guestId ${guestId} to authenticated user ${session.userId}`
      );
    }

    // Создаем код через сервис верификации с идентификатором
    const code = await verificationService.createVerificationCode(
      'telegram',
      session.guestId || tempId,
      session.authenticated ? session.userId : null
    );

    logger.info(
      `[initTelegramAuth] Created verification code for guestId: ${session.guestId || tempId}${session.authenticated ? `, userId: ${session.userId}` : ''}`
    );

    const settings = await getTelegramSettings();
    return {
      verificationCode: code,
      botLink: `https://t.me/${settings.bot_username}`,
    };
  } catch (error) {
    logger.error('Error initializing Telegram auth:', error);
    throw error;
  }
}

function clearSettingsCache() {
  telegramSettingsCache = null;
}

async function getAllBots() {
  const settings = await encryptedDb.getData('telegram_settings', {}, 1, 'id');
  return settings;
}

module.exports = {
  getTelegramSettings,
  getBot,
  stopBot,
  initTelegramAuth,
  clearSettingsCache,
  getAllBots,
};
