const express = require('express');
const router = express.Router();
const multer = require('multer');
const aiAssistant = require('../services/ai-assistant');
const db = require('../db');
const logger = require('../utils/logger');
const { requireAuth } = require('../middleware/auth');
const crypto = require('crypto');

// Настройка multer для обработки файлов в памяти
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });

// Функция для обработки гостевых сообщений после аутентификации
async function processGuestMessages(userId, guestId) {
  try {
    logger.info(`Processing guest messages for user ${userId} with guest ID ${guestId}`);

    // Проверяем, обрабатывались ли уже эти сообщения
    const mappingCheck = await db.getQuery()(
      'SELECT processed FROM guest_user_mapping WHERE guest_id = $1',
      [guestId]
    );

    // Если сообщения уже обработаны, пропускаем
    if (mappingCheck.rows.length > 0 && mappingCheck.rows[0].processed) {
      logger.info(`Guest messages for guest ID ${guestId} were already processed.`);
      return { success: true, message: 'Guest messages already processed' };
    }

    // Проверяем наличие mapping записи и создаем если нет
    if (mappingCheck.rows.length === 0) {
      await db.getQuery()(
        'INSERT INTO guest_user_mapping (user_id, guest_id) VALUES ($1, $2) ON CONFLICT (guest_id) DO UPDATE SET user_id = $1',
        [userId, guestId]
      );
      logger.info(`Created mapping for guest ID ${guestId} to user ${userId}`);
    }

    // Получаем все гостевые сообщения со всеми новыми полями
    const guestMessagesResult = await db.getQuery()(
      `SELECT
         id, guest_id, content, language, is_ai, created_at,
         attachment_filename, attachment_mimetype, attachment_size, attachment_data
       FROM guest_messages WHERE guest_id = $1 ORDER BY created_at ASC`,
      [guestId]
    );

    if (guestMessagesResult.rows.length === 0) {
      logger.info(`No guest messages found for guest ID ${guestId}`);
      const checkResult = await db.getQuery()('SELECT 1 FROM guest_user_mapping WHERE guest_id = $1', [guestId]);
      if (checkResult.rows.length > 0) {
        await db.getQuery()('UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', [guestId]);
        logger.info(`Marked guest mapping as processed (no messages found) for guest ID ${guestId}`);
      } else {
        logger.warn(`Attempted to mark non-existent guest mapping as processed for guest ID ${guestId}`);
      }
      return { success: true, message: 'No guest messages found' };
    }

    const guestMessages = guestMessagesResult.rows;
    logger.info(`Found ${guestMessages.length} guest messages for guest ID ${guestId}`);

    // Создаем новый диалог для этих сообщений
    const firstMessage = guestMessages[0];
    const title = firstMessage.content
      ? (firstMessage.content.length > 30 ? `${firstMessage.content.substring(0, 30)}...` : firstMessage.content)
      : (firstMessage.attachment_filename ? `Файл: ${firstMessage.attachment_filename}` : 'Новый диалог');

    const newConversationResult = await db.getQuery()(
      'INSERT INTO conversations (user_id, title) VALUES ($1, $2) RETURNING *',
      [userId, title]
    );

    const conversation = newConversationResult.rows[0];
    logger.info(`Created new conversation ${conversation.id} for guest messages`);

    // Отслеживаем успешные сохранения сообщений
    const savedMessageIds = [];

    // Обрабатываем каждое гостевое сообщение
    for (const guestMessage of guestMessages) {
      logger.info(`Processing guest message ID ${guestMessage.id}: ${guestMessage.content || guestMessage.attachment_filename || '(empty)'}`);

      try {
        // Сохраняем сообщение пользователя в таблицу messages, включая данные файла
        const userMessageResult = await db.getQuery()(
          `INSERT INTO messages
            (conversation_id, content, sender_type, role, channel, created_at, user_id,
             attachment_filename, attachment_mimetype, attachment_size, attachment_data)
           VALUES
            ($1, $2, 'user', 'user', 'web', $3, $4,
             $5, $6, $7, $8)
           RETURNING *`,
          [
            conversation.id,
            guestMessage.content, // Текст (может быть NULL)
            guestMessage.created_at,
            userId,
            guestMessage.attachment_filename, // Метаданные и данные файла
            guestMessage.attachment_mimetype,
            guestMessage.attachment_size,
            guestMessage.attachment_data // BYTEA
          ]
        );

        const savedUserMessage = userMessageResult.rows[0];
        logger.info(`Saved user message with ID ${savedUserMessage.id}`);
        savedMessageIds.push(guestMessage.id);

        // Получаем ответ от ИИ только для текстовых сообщений
        if (!guestMessage.is_ai && guestMessage.content) {
          logger.info('Getting AI response for:', guestMessage.content);
          const language = guestMessage.language || 'auto';
          // Предполагаем, что aiAssistant.getResponse принимает только текст
          const aiResponseContent = await aiAssistant.getResponse(guestMessage.content, language);
          logger.info('AI response received' + (aiResponseContent ? '' : ' (empty)'), 'for conversation', conversation.id);

          if (aiResponseContent) {
              // Сохраняем ответ от ИИ (у него нет вложений)
              const aiMessageResult = await db.getQuery()(
                `INSERT INTO messages
                  (conversation_id, content, sender_type, role, channel, created_at, user_id)
                 VALUES
                  ($1, $2, 'assistant', 'assistant', 'web', $3, $4)
                 RETURNING *`,
                [
                  conversation.id,
                  aiResponseContent,
                  new Date(),
                  userId
                ]
              );
              logger.info(`Saved AI response with ID ${aiMessageResult.rows[0].id}`);
          }
        } else {
          logger.info(`Skipping AI response for guest message ID ${guestMessage.id} (is_ai: ${guestMessage.is_ai}, hasContent: ${!!guestMessage.content})`);
        }
      } catch (error) {
        logger.error(`Error processing guest message ${guestMessage.id}: ${error.message}`, { stack: error.stack });
        // Продолжаем с другими сообщениями в случае ошибки
      }
    }

    // Удаляем только успешно обработанные гостевые сообщения
    if (savedMessageIds.length > 0) {
      await db.getQuery()('DELETE FROM guest_messages WHERE id = ANY($1::int[])', [savedMessageIds]);
      logger.info(
        `Deleted ${savedMessageIds.length} processed guest messages for guest ID ${guestId}`
      );

      // Помечаем гостевой ID как обработанный
      await db.getQuery()('UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', [
        guestId,
      ]);
      logger.info(`Marked guest mapping as processed for guest ID ${guestId}`);
    } else {
      logger.warn(`No guest messages were successfully processed, skipping deletion for guest ID ${guestId}`);
      // Если не было успешных, все равно пометим как обработанные, чтобы не пытаться снова
      await db.getQuery()('UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', [guestId]);
      logger.info(`Marked guest mapping as processed (no successful messages) for guest ID ${guestId}`);
    }

    return {
      success: true,
      message: `Processed ${savedMessageIds.length} of ${guestMessages.length} guest messages`,
      conversationId: conversation.id,
    };
  } catch (error) {
    logger.error(`Error in processGuestMessages for guest ID ${guestId}: ${error.message}`, { stack: error.stack });
    // Не пробрасываем ошибку дальше, чтобы не прерывать основной поток, но логируем ее
    return { success: false, error: 'Internal error during guest message processing' };
  }
}

// Обработчик для гостевых сообщений
router.post('/guest-message', upload.array('attachments'), async (req, res) => {
  // Логируем полученные данные
  logger.info('Received /guest-message request');
  logger.debug('Request Body:', req.body);
  logger.debug('Request Files:', req.files); // Файлы будут здесь

  try {
    // Извлекаем данные из req.body (текстовые поля)
    const { message, language, guestId: requestGuestId } = req.body;
    const files = req.files; // Файлы извлекаем из req.files
    const file = files && files.length > 0 ? files[0] : null; // Берем только первый файл

    // Валидация: должно быть либо сообщение, либо файл
    if (!message && !file) {
      logger.warn('Guest message attempt without content or file.', { guestId: requestGuestId });
      return res.status(400).json({ success: false, error: 'Требуется текст сообщения или файл.' });
    }
    // Запрещаем и текст, и файл одновременно (согласно новым требованиям)
    if (message && file) {
        logger.warn('Guest message attempt with both text and file.', { guestId: requestGuestId });
        return res.status(400).json({ success: false, error: 'Нельзя отправить текст и файл одновременно.' });
    }

    // Используем гостевой ID из запроса или из сессии, или генерируем новый
    const guestId = requestGuestId || req.session.guestId || crypto.randomBytes(16).toString('hex');

    // Сохраняем/обновляем ID гостя в сессии
    if (req.session.guestId !== guestId) {
      req.session.guestId = guestId;
    }

    // Подготавливаем данные для вставки
    const messageContent = message || ''; // Текст или ПУСТАЯ СТРОКА, если есть файл
    const attachmentFilename = file ? file.originalname : null;
    const attachmentMimetype = file ? file.mimetype : null;
    const attachmentSize = file ? file.size : null;
    const attachmentData = file ? file.buffer : null; // Сам буфер файла

    logger.info('Saving guest message:', {
      guestId,
      message: messageContent,
      file: attachmentFilename,
      mimetype: attachmentMimetype,
      size: attachmentSize
    });

    // Сохраняем сообщение пользователя с текстом или файлом
    const result = await db.getQuery()(
      `INSERT INTO guest_messages
        (guest_id, content, language, is_ai,
         attachment_filename, attachment_mimetype, attachment_size, attachment_data)
       VALUES ($1, $2, $3, false, $4, $5, $6, $7) RETURNING id`,
      [
        guestId,
        messageContent, // Текст сообщения или NULL
        language || 'auto',
        attachmentFilename,
        attachmentMimetype,
        attachmentSize,
        attachmentData // BYTEA данные файла или NULL
      ]
    );

    const savedMessageId = result.rows[0].id;
    logger.info('Guest message saved with ID:', savedMessageId);

    // Сохраняем сессию после успешной операции с БД
    try {
      await new Promise((resolve, reject) => {
        req.session.save((err) => {
          if (err) return reject(err);
          resolve();
        });
      });
      logger.info('Session saved after guest message');
    } catch (sessionError) {
      logger.error('Error saving session after guest message:', sessionError);
      // Не прерываем ответ пользователю из-за ошибки сессии
    }

    res.json({
      success: true,
      messageId: savedMessageId, // Возвращаем ID сохраненного сообщения
      guestId: guestId // Возвращаем использованный guestId
    });
  } catch (error) {
    logger.error('Error saving guest message:', error);
    res.status(500).json({ success: false, error: 'Ошибка сохранения гостевого сообщения' });
  }
});

// Обработчик для сообщений аутентифицированных пользователей
router.post('/message', requireAuth, upload.array('attachments'), async (req, res) => {
  logger.info('Received /message request');
  logger.debug('Request Body:', req.body);
  logger.debug('Request Files:', req.files);

  const userId = req.session.userId;
  const { message, language, conversationId: convIdFromRequest } = req.body;
  const files = req.files;
  const file = files && files.length > 0 ? files[0] : null;

  // Валидация: должно быть либо сообщение, либо файл
  if (!message && !file) {
    logger.warn('Authenticated message attempt without content or file.', { userId });
    return res.status(400).json({ success: false, error: 'Требуется текст сообщения или файл.' });
  }
  // Запрещаем и текст, и файл одновременно
  if (message && file) {
      logger.warn('Authenticated message attempt with both text and file.', { userId });
      return res.status(400).json({ success: false, error: 'Нельзя отправить текст и файл одновременно.' });
  }

  let conversationId = convIdFromRequest;
  let conversation = null;

  try {
    // Найти или создать диалог
    if (conversationId) {
      const convResult = await db.getQuery()(
        'SELECT * FROM conversations WHERE id = $1 AND user_id = $2',
        [conversationId, userId]
      );
      if (convResult.rows.length === 0) {
        logger.warn('Conversation not found or access denied', { conversationId, userId });
        return res.status(404).json({ success: false, error: 'Диалог не найден или доступ запрещен' });
      }
      conversation = convResult.rows[0];
    } else {
      // Создаем новый диалог, если ID не предоставлен
      const title = message
        ? (message.length > 50 ? `${message.substring(0, 50)}...` : message)
        : (file ? `Файл: ${file.originalname}` : 'Новый диалог');

      const newConvResult = await db.getQuery()(
        'INSERT INTO conversations (user_id, title) VALUES ($1, $2) RETURNING *',
        [userId, title]
      );
      conversation = newConvResult.rows[0];
      conversationId = conversation.id;
      logger.info('Created new conversation', { conversationId, userId });
    }

    // Подготавливаем данные для вставки сообщения пользователя
    const messageContent = message || ''; // Текст или ПУСТАЯ СТРОКА, если есть файл
    const attachmentFilename = file ? file.originalname : null;
    const attachmentMimetype = file ? file.mimetype : null;
    const attachmentSize = file ? file.size : null;
    const attachmentData = file ? file.buffer : null;

    // Сохраняем сообщение пользователя
    const userMessageResult = await db.getQuery()(
      `INSERT INTO messages
         (conversation_id, user_id, content, sender_type, role, channel,
          attachment_filename, attachment_mimetype, attachment_size, attachment_data)
       VALUES ($1, $2, $3, 'user', 'user', 'web', $4, $5, $6, $7)
       RETURNING *`,
      [
        conversationId,
        userId,
        messageContent,
        attachmentFilename,
        attachmentMimetype,
        attachmentSize,
        attachmentData
      ]
    );
    const userMessage = userMessageResult.rows[0];
    logger.info('User message saved', { messageId: userMessage.id, conversationId });

    // Получаем ответ от ИИ, только если это было текстовое сообщение
    let aiMessage = null;
    if (messageContent) { // Только для текстовых сообщений
      try {
        const detectedLanguage = language === 'auto' ? aiAssistant.detectLanguage(messageContent) : language;
        logger.info('Getting AI response for:', messageContent);
        const aiResponseContent = await aiAssistant.getResponse(messageContent, detectedLanguage);
        logger.info('AI response received' + (aiResponseContent ? '' : ' (empty)'), { conversationId });

        if (aiResponseContent) {
          const aiMessageResult = await db.getQuery()(
            `INSERT INTO messages
               (conversation_id, user_id, content, sender_type, role, channel)
             VALUES ($1, $2, $3, 'assistant', 'assistant', 'web')
             RETURNING *`,
            [conversationId, userId, aiResponseContent]
          );
          aiMessage = aiMessageResult.rows[0];
          logger.info('AI response saved', { messageId: aiMessage.id, conversationId });
        }
      } catch (aiError) {
        logger.error('Error getting or saving AI response:', aiError);
        // Не прерываем основной ответ, но логируем ошибку
      }
    }

    // Форматируем ответ для фронтенда
    const formatMessageForFrontend = (msg) => {
      if (!msg) return null;
      const formatted = {
        id: msg.id,
        conversation_id: msg.conversation_id,
        user_id: msg.user_id,
        content: msg.content,
        sender_type: msg.sender_type,
        role: msg.role,
        channel: msg.channel,
        created_at: msg.created_at,
        attachments: null // Инициализируем как null
      };
      // Добавляем информацию о файле, если она есть
      if (msg.attachment_filename) {
        formatted.attachments = [{
          originalname: msg.attachment_filename,
          mimetype: msg.attachment_mimetype,
          size: msg.attachment_size,
          // НЕ передаем attachment_data обратно в ответе на POST
        }];
      }
      return formatted;
    };

    res.json({
      success: true,
      conversationId: conversationId,
      userMessage: formatMessageForFrontend(userMessage),
      aiMessage: formatMessageForFrontend(aiMessage),
    });
  } catch (error) {
    logger.error('Error processing authenticated message:', error);
    res.status(500).json({ success: false, error: 'Ошибка обработки сообщения' });
  }
});

// Добавьте этот маршрут для проверки доступных моделей
router.get('/models', async (req, res) => {
  try {
    const models = await aiAssistant.getAvailableModels();

    res.json({
      success: true,
      models: models,
    });
  } catch (error) {
    console.error('Ошибка при получении списка моделей:', error);
    res.status(500).json({ success: false, message: 'Ошибка сервера' });
  }
});

// Получение истории сообщений
router.get('/history', requireAuth, async (req, res) => {
  const userId = req.session.userId;
  // Параметры пагинации
  const limit = parseInt(req.query.limit, 10) || 30;
  const offset = parseInt(req.query.offset, 10) || 0;
  // Флаг для запроса только количества
  const countOnly = req.query.count_only === 'true';
  // Опциональный ID диалога
  const conversationId = req.query.conversation_id;

  try {
    // Если нужен только подсчет
    if (countOnly) {
      let countQuery = 'SELECT COUNT(*) FROM messages WHERE user_id = $1';
      let countParams = [userId];
      if (conversationId) {
        countQuery += ' AND conversation_id = $2';
        countParams.push(conversationId);
      }
      const countResult = await db.getQuery()(countQuery, countParams);
      const totalCount = parseInt(countResult.rows[0].count, 10);
      return res.json({ success: true, count: totalCount });
    }

    // Формируем основной запрос
    let query = `
      SELECT
        id,
        conversation_id,
        user_id,
        content,
        sender_type,
        role,
        channel,
        created_at,
        attachment_filename,
        attachment_mimetype,
        attachment_size,
        attachment_data -- Выбираем и данные файла
      FROM messages
      WHERE user_id = $1
    `;
    const params = [userId];

    // Добавляем фильтр по диалогу, если нужно
    if (conversationId) {
      query += ' AND conversation_id = $2';
      params.push(conversationId);
    }

    // Добавляем сортировку и пагинацию
    query += ' ORDER BY created_at ASC LIMIT $' + (params.length + 1) + ' OFFSET $' + (params.length + 2);
    params.push(limit);
    params.push(offset);

    logger.debug('Executing history query:', { query, params });

    const result = await db.getQuery()(query, params);

    // Обрабатываем результаты для фронтенда
    const messages = result.rows.map(msg => {
      const formatted = {
        id: msg.id,
        conversation_id: msg.conversation_id,
        user_id: msg.user_id,
        content: msg.content,
        sender_type: msg.sender_type,
        role: msg.role,
        channel: msg.channel,
        created_at: msg.created_at,
        attachments: null // Инициализируем
      };

      // Если есть данные файла, добавляем их в attachments
      if (msg.attachment_data) {
        formatted.attachments = [{
          originalname: msg.attachment_filename,
          mimetype: msg.attachment_mimetype,
          size: msg.attachment_size,
          // Кодируем Buffer в Base64 для передачи на фронтенд
          data_base64: msg.attachment_data.toString('base64')
        }];
      }
      // Не забываем удалить поле attachment_data из итогового объекта,
      // так как оно уже обработано и не нужно в сыром виде на фронте
      // (хотя map и так создает новый объект, это для ясности)
      delete formatted.attachment_data;

      return formatted;
    });

    // Получаем общее количество сообщений для пагинации (если не запрашивали только количество)
    let totalCountQuery = 'SELECT COUNT(*) FROM messages WHERE user_id = $1';
    let totalCountParams = [userId];
    if (conversationId) {
      totalCountQuery += ' AND conversation_id = $2';
      totalCountParams.push(conversationId);
    }
    const totalCountResult = await db.getQuery()(totalCountQuery, totalCountParams);
    const totalMessages = parseInt(totalCountResult.rows[0].count, 10);

    logger.info(`Returning message history for user ${userId}`, { count: messages.length, offset, limit, total: totalMessages });

    res.json({
      success: true,
      messages: messages,
      offset: offset,
      limit: limit,
      total: totalMessages
    });

  } catch (error) {
    logger.error(`Error fetching message history for user ${userId}: ${error.message}`, { stack: error.stack });
    res.status(500).json({ success: false, error: 'Ошибка получения истории сообщений' });
  }
});

// Экспортируем маршрутизатор и функцию processGuestMessages отдельно
module.exports = router;
module.exports.processGuestMessages = processGuestMessages;
