/**
 * Copyright (c) 2024-2026 Тарабанов Александр Викторович
 * 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/VC-HB3-Accelerator
 */

const db = require('../db');
const logger = require('../utils/logger');
const encryptionUtils = require('../utils/encryptionUtils');
const crypto = require('crypto');
const universalMediaProcessor = require('./UniversalMediaProcessor');

/**
 * Универсальный сервис для обработки гостевых сообщений
 * Работает со всеми каналами: web, telegram, email
 */
class UniversalGuestService {
  /**
   * Создать унифицированный идентификатор
   * @param {string} channel - 'web', 'telegram', 'email'
   * @param {string} rawId - Исходный ID
   * @returns {string} - "channel:rawId"
   */
  createIdentifier(channel, rawId) {
    if (!channel || !rawId) {
      throw new Error('Channel and rawId are required');
    }
    return `${channel}:${rawId}`;
  }

  /**
   * Сгенерировать гостевой ID для Web
   * @returns {string} - "guest_abc123..."
   */
  generateWebGuestId() {
    return `guest_${crypto.randomBytes(16).toString('hex')}`;
  }

  /**
   * Разобрать идентификатор на части
   * @param {string} identifier - "channel:id"
   * @returns {Object} - {channel, id}
   */
  parseIdentifier(identifier) {
    const parts = identifier.split(':');
    if (parts.length < 2) {
      throw new Error(`Invalid identifier format: ${identifier}`);
    }
    return {
      channel: parts[0],
      id: parts.slice(1).join(':') // На случай если в ID есть двоеточие (email)
    };
  }

  /**
   * Проверить, является ли идентификатор гостевым
   * @param {string} identifier
   * @returns {boolean}
   */
  isGuest(identifier) {
    if (!identifier || typeof identifier !== 'string') {
      return true; // По умолчанию считаем гостем
    }

    // Если нет user_id в БД - это гость
    // Для упрощения: любой identifier без wallet в user_identities = гость
    return true; // Пока всегда true, позже добавим проверку через БД
  }

  /**
   * Сохранить сообщение гостя
   * @param {Object} messageData
   * @returns {Promise<Object>}
   */
  async saveMessage(messageData) {
    try {
      const {
        identifier,
        content,
        channel,
        metadata = {},
        attachment_filename,
        attachment_mimetype,
        attachment_size,
        attachment_data,
        contentData = null // Новый параметр для структурированного контента
      } = messageData;

      const encryptionKey = encryptionUtils.getEncryptionKey();

      // Обработка контента через UniversalMediaProcessor
      let processedContent = null;
      let finalContent = content;
      let finalMetadata = { ...metadata };

      if (contentData) {
        processedContent = await universalMediaProcessor.processCombinedContent(contentData);
        // Если есть и текст, и файлы - объединяем их
        if (content && processedContent.summary) {
          finalContent = `${content}\n\n[Прикрепленные файлы: ${processedContent.summary}]`;
        } else if (processedContent.summary) {
          // Только файлы без текста
          finalContent = processedContent.summary;
        }
        finalMetadata.mediaSummary = processedContent.summary;
      } else if (attachment_data) {
        // Если есть только одно вложение без contentData, обрабатываем его
        processedContent = await universalMediaProcessor.processFile(
          attachment_data,
          attachment_filename,
          {
            mimeType: attachment_mimetype,
            originalSize: attachment_size
          }
        );
        finalContent = content || processedContent.content;
        finalMetadata.mediaSummary = processedContent.content;
      }

      const { rows } = await db.getQuery()(
        `INSERT INTO unified_guest_messages (
          identifier_encrypted,
          channel,
          content_encrypted,
          is_ai,
          metadata,
          attachment_filename_encrypted,
          attachment_mimetype_encrypted,
          attachment_size,
          attachment_data,
          content_type,
          attachments,
          media_metadata,
          created_at
        ) VALUES (
          encrypt_text($1, $12),
          $2,
          encrypt_text($3, $12),
          $4,
          $5,
          encrypt_text($6, $12),
          encrypt_text($7, $12),
          $8,
          $9,
          $10,
          $11,
          $13,
          NOW()
        ) RETURNING id, created_at`,
        [
          identifier,
          channel,
          finalContent,
          false, // is_ai = false (это сообщение от гостя)
          JSON.stringify(finalMetadata),
          attachment_filename || null,
          attachment_mimetype || null,
          attachment_size || null,
          attachment_data || null,
          processedContent ? processedContent.type : 'text',
          processedContent ? JSON.stringify(processedContent.parts) : null,
          encryptionKey,
          JSON.stringify(finalMetadata)
        ]
      );

      const messageId = rows[0].id;
      
      // Если есть медиа-файлы, сохраняем их метаданные
      if (processedContent && processedContent.type === 'combined') {
        await this.saveMediaFiles(messageId, processedContent.parts, identifier, channel);
      }
      
      logger.info(`[UniversalGuestService] Сохранено сообщение гостя: ${identifier}, id: ${messageId}, тип: ${processedContent ? processedContent.type : 'text'}`);

      return {
        success: true,
        messageId: messageId,
        identifier,
        created_at: rows[0].created_at,
        processedContent
      };

    } catch (error) {
      logger.error('[UniversalGuestService] Ошибка сохранения сообщения гостя:', error);
      throw error;
    }
  }

  /**
   * Сохраняет метаданные медиа-файлов
   */
  async saveMediaFiles(messageId, contentParts, identifier, channel) {
    try {
      for (const part of contentParts) {
        if (part.type !== 'text' && part.file) {
          await db.getQuery()(
            `INSERT INTO media_files 
             (message_id, file_name, original_name, file_path, file_size, file_type, 
              mime_type, identifier, channel, metadata) 
             VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`,
            [
              messageId,
              part.file.savedName,
              part.file.originalName,
              part.file.path,
              part.file.size,
              part.type,
              part.metadata?.mimeType || null, // Сохраняем реальный MIME-тип
              identifier,
              channel,
              JSON.stringify(part.metadata)
            ]
          );
        }
      }
      logger.info(`[UniversalGuestService] Сохранены метаданные медиа-файлов для сообщения ${messageId}`);
    } catch (error) {
      logger.error(`[UniversalGuestService] Ошибка сохранения метаданных медиа:`, error);
    }
  }

  /**
   * Сохранить AI ответ гостю
   * @param {Object} responseData
   * @returns {Promise<Object>}
   */
  async saveAiResponse(responseData) {
    try {
      const {
        identifier,
        content,
        channel,
        metadata = {}
      } = responseData;

      const encryptionKey = encryptionUtils.getEncryptionKey();

      const { rows } = await db.getQuery()(
        `INSERT INTO unified_guest_messages (
          identifier_encrypted,
          channel,
          content_encrypted,
          is_ai,
          metadata,
          created_at
        ) VALUES (
          encrypt_text($1, $6),
          $2,
          encrypt_text($3, $6),
          $4,
          $5,
          NOW()
        ) RETURNING id, created_at`,
        [
          identifier,
          channel,
          content,
          true, // is_ai = true (это ответ AI)
          JSON.stringify(metadata),
          encryptionKey
        ]
      );

      logger.info(`[UniversalGuestService] Сохранен AI ответ для гостя: ${identifier}, id: ${rows[0].id}`);

      return {
        success: true,
        messageId: rows[0].id,
        identifier,
        created_at: rows[0].created_at
      };

    } catch (error) {
      logger.error('[UniversalGuestService] Ошибка сохранения AI ответа:', error);
      throw error;
    }
  }

  /**
   * Получить историю сообщений гостя
   * @param {string} identifier - "channel:id"
   * @returns {Promise<Array>} - [{role: 'user'/'assistant', content}]
   */
  async getHistory(identifier) {
    try {
      const encryptionKey = encryptionUtils.getEncryptionKey();

      const { rows } = await db.getQuery()(
        `SELECT 
          decrypt_text(content_encrypted, $2) as content,
          is_ai,
          created_at
         FROM unified_guest_messages
         WHERE decrypt_text(identifier_encrypted, $2) = $1
         ORDER BY created_at ASC`,
        [identifier, encryptionKey]
      );

      // Преобразуем в формат для AI
      const history = rows.map(row => ({
        role: row.is_ai ? 'assistant' : 'user',
        content: row.content
      }));

      logger.info(`[UniversalGuestService] Загружена история для ${identifier}: ${history.length} сообщений`);

      return history;

    } catch (error) {
      logger.error('[UniversalGuestService] Ошибка получения истории:', error);
      throw error;
    }
  }

  /**
   * Извлечь имя гостя из текста сообщения через ИИ и сохранить в metadata
   * @param {string} identifier - Идентификатор гостя
   * @param {string} content - Текст сообщения
   * @param {string} channel - Канал
   * @returns {Promise<void>}
   */
  async extractAndSaveGuestName(identifier, content, channel) {
    try {
      if (!content || !content.trim()) {
        return; // Нет текста для анализа
      }

      const encryptionKey = encryptionUtils.getEncryptionKey();

      // Находим первое сообщение гостя
      const firstMessageResult = await db.getQuery()(
        `WITH decrypted_guest AS (
          SELECT 
            id,
            decrypt_text(identifier_encrypted, $2) as guest_identifier,
            channel,
            metadata
          FROM unified_guest_messages
          WHERE user_id IS NULL
        )
        SELECT 
          id as first_message_id,
          metadata
        FROM decrypted_guest
        WHERE guest_identifier = $1 AND channel = $3
        ORDER BY id ASC
        LIMIT 1`,
        [identifier, encryptionKey, channel]
      );

      if (firstMessageResult.rows.length === 0) {
        return; // Гость не найден
      }

      const firstMessage = firstMessageResult.rows[0];
      let metadata = firstMessage.metadata || {};

      // Если metadata - строка, парсим её
      if (typeof metadata === 'string') {
        try {
          metadata = JSON.parse(metadata);
        } catch (e) {
          metadata = {};
        }
      }

      // Если уже есть кастомное имя, не извлекаем заново
      if (metadata.custom_name) {
        logger.info(`[UniversalGuestService] У гостя ${identifier} уже есть кастомное имя: ${metadata.custom_name}`);
        return;
      }

      // Используем существующий сервис для извлечения имени
      const profileAnalysisService = require('./profileAnalysisService');
      const nameResult = await profileAnalysisService.extractName(content);

      // Проверяем результат извлечения имени
      if (!nameResult || !nameResult.name || !nameResult.should_update_name) {
        logger.info(`[UniversalGuestService] Имя не найдено в сообщении гостя ${identifier} (confidence: ${nameResult?.confidence || 0})`);
        return;
      }

      const extractedName = nameResult.name;

      // Разбиваем имя на части
      const nameParts = extractedName.split(' ');
      const firstName = nameParts[0] || '';
      const lastName = nameParts.slice(1).join(' ') || '';

      // Сохраняем имя в metadata
      metadata.custom_name = extractedName;
      metadata.custom_first_name = firstName;
      metadata.custom_last_name = lastName;

      // Обновляем metadata первого сообщения гостя
      await db.getQuery()(
        `UPDATE unified_guest_messages 
         SET metadata = $1 
         WHERE id = $2`,
        [JSON.stringify(metadata), firstMessage.first_message_id]
      );

      logger.info(`[UniversalGuestService] Имя гостя ${identifier} извлечено и сохранено: ${extractedName}`);

    } catch (error) {
      logger.error('[UniversalGuestService] Ошибка извлечения имени гостя:', error);
      throw error;
    }
  }

  /**
   * Обработать сообщение гостя (сохранить + получить AI ответ)
   * @param {Object} messageData
   * @returns {Promise<Object>}
   */
  async processMessage(messageData) {
    try {
      const { identifier, content, channel, contentData } = messageData;

      logger.info(`[UniversalGuestService] Обработка сообщения гостя: ${identifier}`);

      // 0.5. Проверяем, нужно ли автоматически подписать согласие при ответе
      // Загружаем историю для проверки последнего сообщения
      const previousHistory = await this.getHistory(identifier);
      
      // Если в истории есть системное сообщение о согласиях, автоматически подписываем при ответе
      if (previousHistory.length > 0) {
        const consentService = require('./consentService');
        const [provider, providerId] = identifier?.split(':') || [];
        let walletAddress = null;
        
        if (provider === 'web' && providerId?.startsWith('guest_')) {
          walletAddress = providerId;
        }
        
        // Проверяем, было ли последнее сообщение системным с согласием
        const lastMessage = previousHistory[previousHistory.length - 1];
        const hasConsentSystemMessage = lastMessage && 
          (lastMessage.role === 'system' || lastMessage.consentRequired);
        
        if (hasConsentSystemMessage) {
          // Проверяем текущие согласия
          const consentCheck = await consentService.checkConsents({
            userId: null,
            walletAddress
          });
          
          // Если согласия нужны, автоматически подписываем
          if (consentCheck.needsConsent) {
            logger.info(`[UniversalGuestService] Автоматическое подписание согласий при ответе гостя ${identifier}`);
            
            const consentDocuments = await consentService.getConsentDocuments(consentCheck.missingConsents);
            const documentIds = consentDocuments.map(doc => doc.id);
            const consentTypes = consentDocuments.map(doc => doc.consentType).filter(type => type);
            
            if (documentIds.length > 0 && consentTypes.length > 0) {
              const db = require('../db');
              try {
                // Для гостей используем wallet_address в формате guest_ID
                await db.getQuery()(
                  `INSERT INTO consent_logs (wallet_address, document_id, document_title, consent_type, status, signed_at, channel, ip_address, created_at, updated_at)
                   SELECT $1, unnest($2::int[]), unnest($3::text[]), unnest($4::text[]), 'granted', NOW(), 'web', NULL, NOW(), NOW()
                   ON CONFLICT (wallet_address, consent_type, document_id) 
                   DO UPDATE SET 
                     status = 'granted',
                     signed_at = NOW(),
                     revoked_at = NULL,
                     updated_at = NOW()
                   WHERE consent_logs.wallet_address = $1 AND consent_logs.consent_type = EXCLUDED.consent_type`,
                  [
                    walletAddress,
                    documentIds,
                    consentDocuments.map(doc => doc.title),
                    consentTypes
                  ]
                );
                logger.info(`[UniversalGuestService] Согласия автоматически подписаны для гостя ${identifier}`);
              } catch (consentError) {
                logger.error(`[UniversalGuestService] Ошибка автоматического подписания согласий:`, consentError);
              }
            }
          }
        }
      }

      // 1. Сохраняем сообщение гостя
      const saveResult = await this.saveMessage(messageData);
      const processedContent = saveResult.processedContent;

      // 1.5. Извлекаем имя из текста сообщения через ИИ (если это первое сообщение гостя)
      await this.extractAndSaveGuestName(identifier, content, channel).catch(error => {
        // Не критично, если не удалось извлечь имя - просто логируем
        logger.warn(`[UniversalGuestService] Ошибка извлечения имени гостя:`, error);
      });

      // 2. Загружаем историю для контекста (заново, так как могли добавиться сообщения)
      const conversationHistory = await this.getHistory(identifier);

      // 3. Генерируем AI ответ
      const aiAssistant = require('./ai-assistant');
      
      // Формируем полное описание сообщения для AI
      let fullMessageContent = content;
      if (processedContent && processedContent.summary) {
        // Если есть медиа, добавляем информацию о них
        fullMessageContent = content ? `${content}\n\n[Прикрепленные файлы: ${processedContent.summary}]` : processedContent.summary;
      }
      
      const aiResponse = await aiAssistant.generateResponse({
        channel: channel,
        messageId: `guest_${identifier}_${Date.now()}`,
        userId: null, // Для гостей передаем null, чтобы не использовать identifier как user_id
        userQuestion: fullMessageContent,
        conversationHistory: conversationHistory,
        metadata: { 
          isGuest: true,
          hasMedia: !!processedContent,
          mediaSummary: processedContent?.summary,
          guestIdentifier: identifier // Сохраняем identifier в metadata для логирования
        }
      });

      if (aiResponse && aiResponse.disabled) {
        logger.info(`[UniversalGuestService] AI ассистент отключен для канала ${channel}. Ответ не формируется.`);
        return {
          success: true,
          identifier,
          aiResponse: null,
          assistantDisabled: true
        };
      }

      if (!aiResponse || !aiResponse.success) {
        logger.warn(`[UniversalGuestService] AI не вернул ответ для ${identifier}`);
        return {
          success: false,
          reason: aiResponse?.reason || 'no_ai_response'
        };
      }

      // 4. Сохраняем AI ответ
      let finalAiResponse = aiResponse.response;
      await this.saveAiResponse({
        identifier,
        content: finalAiResponse,
        channel,
        metadata: messageData.metadata || {}
      });

      logger.info(`[UniversalGuestService] Сообщение гостя ${identifier} обработано успешно`);

      const result = {
        success: true,
        identifier,
        aiResponse: {
          response: finalAiResponse,
          ragData: aiResponse.ragData
        }
      };

      return result;

    } catch (error) {
      logger.error('[UniversalGuestService] Ошибка обработки сообщения гостя:', error);
      throw error;
    }
  }

  /**
   * Мигрировать историю гостя в user_id
   * @param {string} identifier - "channel:id"
   * @param {number} userId
   * @returns {Promise<Object>}
   */
  async migrateToUser(identifier, userId) {
    try {
      logger.info(`[UniversalGuestService] Миграция истории ${identifier} → user ${userId}`);

      const encryptionKey = encryptionUtils.getEncryptionKey();

      // 1. Получаем все сообщения гостя
      const { rows: messages } = await db.getQuery()(
        `SELECT 
          decrypt_text(identifier_encrypted, $2) as identifier,
          channel,
          decrypt_text(content_encrypted, $2) as content,
          is_ai,
          metadata,
          decrypt_text(attachment_filename_encrypted, $2) as attachment_filename,
          decrypt_text(attachment_mimetype_encrypted, $2) as attachment_mimetype,
          attachment_size,
          attachment_data,
          created_at
         FROM unified_guest_messages
         WHERE decrypt_text(identifier_encrypted, $2) = $1
         ORDER BY created_at ASC`,
        [identifier, encryptionKey]
      );

      if (messages.length === 0) {
        logger.info(`[UniversalGuestService] Нет сообщений для миграции`);
        return { migrated: 0, skipped: 0, conversationId: null };
      }

      // 2. Создаем беседу для пользователя
      const conversationService = require('./conversationService');
      const conversation = await conversationService.getOrCreateConversation(
        userId,
        'Перенесенная беседа'
      );
      const conversationId = conversation.id;

      let migrated = 0;
      let skipped = 0;

      // 3. Переносим каждое сообщение
      for (const msg of messages) {
        try {
          const senderType = msg.is_ai ? 'assistant' : 'user';
          const role = msg.is_ai ? 'assistant' : 'user';
          const direction = msg.is_ai ? 'outgoing' : 'incoming';

          await db.getQuery()(
            `INSERT INTO messages (
              conversation_id,
              sender_id,
              sender_type_encrypted,
              content_encrypted,
              channel_encrypted,
              role_encrypted,
              direction_encrypted,
              attachment_filename,
              attachment_mimetype,
              attachment_size,
              attachment_data,
              message_type,
              user_id,
              role,
              direction,
              created_at
            ) VALUES (
              $1, $2,
              encrypt_text($3, $17),
              encrypt_text($4, $17),
              encrypt_text($5, $17),
              encrypt_text($6, $17),
              encrypt_text($7, $17),
              $8, $9, $10, $11,
              $12, $13, $14, $15,
              $16
            )`,
            [
              conversationId,
              userId, // sender_id
              senderType,
              msg.content,
              msg.channel,
              role,
              direction,
              msg.attachment_filename,
              msg.attachment_mimetype,
              msg.attachment_size,
              msg.attachment_data,
              'user_chat', // message_type для мигрированных сообщений (личный чат с ИИ)
              userId, // user_id
              role, // role (незашифрованное)
              direction, // direction (незашифрованное)
              msg.created_at,
              encryptionKey
            ]
          );

          migrated++;

        } catch (error) {
          logger.error('[UniversalGuestService] Ошибка переноса сообщения:', error);
          skipped++;
        }
      }

      // 4. Удаляем гостевые сообщения после успешного переноса
      if (migrated > 0) {
        await db.getQuery()(
          `DELETE FROM unified_guest_messages
           WHERE decrypt_text(identifier_encrypted, $2) = $1`,
          [identifier, encryptionKey]
        );

        // Сохраняем маппинг
        const { channel } = this.parseIdentifier(identifier);
        await db.getQuery()(
          `INSERT INTO unified_guest_mapping (
            user_id,
            identifier_encrypted,
            channel,
            processed,
            processed_at
          ) VALUES (
            $1,
            encrypt_text($2, $4),
            $3,
            true,
            NOW()
          )
          ON CONFLICT (identifier_encrypted, channel) DO NOTHING`,
          [userId, identifier, channel, encryptionKey]
        );
      }

      // 5. Переносим согласия гостя на пользователя, если они есть
      // Согласия могут быть связаны с гостевой сессией через wallet_address = "guest_${guestId}"
      // Только для web-гостей (формат: web:guest_xxx)
      try {
        const [channel, guestId] = identifier.split(':');
        
        // Мигрируем согласия только для web-гостей
        if (channel !== 'web' || !guestId?.startsWith('guest_')) {
          logger.info(`[UniversalGuestService] Пропуск миграции согласий для ${identifier} (не web-гость)`);
          return { migrated, skipped };
        }
        
        // Ищем согласия по гостевому идентификатору в формате "guest_${guestId}"
        const guestWalletAddress = guestId; // Уже в формате "guest_xxx"
        
        const { rows: guestConsents } = await db.getQuery()(`
          SELECT id, consent_type, document_id, document_title, status, signed_at, ip_address, user_agent, channel as consent_channel
          FROM consent_logs
          WHERE wallet_address = $1
            AND status = 'granted'
            AND (user_id IS NULL OR user_id = $2)
        `, [guestWalletAddress, userId]);
        
        if (guestConsents.length > 0) {
          logger.info(`[UniversalGuestService] Найдено ${guestConsents.length} согласий для переноса`);
          
          // Переносим согласия на пользователя
          // Обновляем wallet_address на нормализованный адрес кошелька пользователя, если он есть
          const identityService = require('./identity-service');
          const walletIdentity = await identityService.findIdentity(userId, 'wallet');
          const normalizedWalletAddress = walletIdentity?.provider_id || null;
          
          for (const consent of guestConsents) {
            await db.getQuery()(`
              UPDATE consent_logs
              SET user_id = $1,
                  wallet_address = COALESCE($2, wallet_address),
                  updated_at = NOW()
              WHERE id = $3
            `, [userId, normalizedWalletAddress, consent.id]);
          }
          
          logger.info(`[UniversalGuestService] Перенесено ${guestConsents.length} согласий на user ${userId}`);
        }
      } catch (consentError) {
        // Не критично, если не удалось перенести согласия - просто логируем
        logger.warn(`[UniversalGuestService] Ошибка переноса согласий (не критично):`, consentError);
      }

      logger.info(`[UniversalGuestService] Миграция завершена: ${migrated} перенесено, ${skipped} пропущено`);

      return {
        success: true,
        migrated,
        skipped,
        total: messages.length,
        conversationId
      };

    } catch (error) {
      logger.error('[UniversalGuestService] Ошибка миграции истории:', error);
      throw error;
    }
  }

  /**
   * Получить статистику по гостям
   * @returns {Promise<Object>}
   */
  async getStats() {
    try {
      const { rows } = await db.getQuery()(
        `SELECT 
          COUNT(DISTINCT identifier_encrypted) as unique_guests,
          COUNT(*) FILTER (WHERE is_ai = false) as user_messages,
          COUNT(*) FILTER (WHERE is_ai = true) as ai_responses,
          MAX(created_at) as last_activity
         FROM unified_guest_messages`
      );

      return rows[0];

    } catch (error) {
      logger.error('[UniversalGuestService] Ошибка получения статистики:', error);
      throw error;
    }
  }
}

module.exports = new UniversalGuestService();

