/**
 * 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 logger = require('../utils/logger');
const encryptedDb = require('./encryptedDatabaseService');
const universalGuestService = require('./UniversalGuestService');
const db = require('../db');

/**
 * Сервис для работы с сессиями пользователей
 */
class SessionService {
  /**
   * Сохраняет сессию, обрабатывая ошибки и логируя результат
   * @param {Object} session - Объект сессии Express
   * @returns {Promise<boolean>} - Успешно ли сохранена сессия
   */
  async saveSession(session) {
    try {
      return new Promise((resolve, reject) => {
        session.save((err) => {
          if (err) {
            logger.error('Error saving session:', err);
            reject(err);
          } else {
            resolve(true);
          }
        });
      });
    } catch (error) {
      logger.error(`[saveSession] Error:`, error);
      throw error;
    }
  }

  /**
   * Связывает гостевые сообщения с пользователем после аутентификации
   * @param {Object} session - Объект сессии Express
   * @param {number} userId - ID пользователя
   * @returns {Promise<Object>} - Результат операции
   */
  async linkGuestMessages(session, userId) {
    try {
      // Получаем все гостевые ID для текущего пользователя из таблицы
      const guestIdsResult = await encryptedDb.getData('unified_guest_mapping', { user_id: userId });
      const userGuestIds = guestIdsResult.map((row) => row.identifier_encrypted);

      // Собираем все гостевые ID, которые нужно обработать
      const guestIdsToProcess = new Set();

      // Добавляем текущий гостевой ID, если он есть и не обработан в БД
      if (session.guestId) {
        const isProcessed = await this.isGuestIdProcessed(session.guestId);
        if (!isProcessed) {
          guestIdsToProcess.add(session.guestId);

          // Записываем связь с пользователем в новую таблицу
          // НЕ используем encryptedDb.saveData, т.к. identifier_encrypted требует ручного шифрования
          const encryptionUtils = require('../utils/encryptionUtils');
          const encryptionKey = encryptionUtils.getEncryptionKey();
          await db.getQuery()(
            `INSERT INTO unified_guest_mapping (user_id, identifier_encrypted, channel, created_at)
             VALUES ($1, encrypt_text($2, $4), $3, NOW())
             ON CONFLICT (identifier_encrypted, channel) DO NOTHING`,
            [userId, `web:${session.guestId}`, 'web', encryptionKey]
          );
        }
      }

      // Добавляем предыдущий гостевой ID, если он есть и не обработан в БД
      if (session.previousGuestId) {
        const isProcessed = await this.isGuestIdProcessed(session.previousGuestId);
        if (!isProcessed) {
          guestIdsToProcess.add(session.previousGuestId);

          // Записываем связь с пользователем в новую таблицу
          // НЕ используем encryptedDb.saveData, т.к. identifier_encrypted требует ручного шифрования
          const encryptionUtils = require('../utils/encryptionUtils');
          const encryptionKey = encryptionUtils.getEncryptionKey();
          await db.getQuery()(
            `INSERT INTO unified_guest_mapping (user_id, identifier_encrypted, channel, created_at)
             VALUES ($1, encrypt_text($2, $4), $3, NOW())
             ON CONFLICT (identifier_encrypted, channel) DO NOTHING`,
            [userId, `web:${session.previousGuestId}`, 'web', encryptionKey]
          );
        }
      }

      // Добавляем все гостевые ID пользователя из таблицы, которые еще не обработаны
      for (const guestId of userGuestIds) {
        const isProcessed = await this.isGuestIdProcessed(guestId);
        if (!isProcessed) {
          guestIdsToProcess.add(guestId);
        }
      }

      // Логируем только если есть что обрабатывать
      if (guestIdsToProcess.size > 0) {
        logger.info(
          `[SessionService] Linking ${guestIdsToProcess.size} guest IDs to user ${userId}: ${Array.from(guestIdsToProcess).join(', ')}`
        );

        // Обрабатываем сообщения для каждого гостевого ID (используем UniversalGuestService)
        for (const guestId of guestIdsToProcess) {
          const identifier = `web:${guestId}`; // Старые гости всегда из web
          await universalGuestService.migrateToUser(identifier, userId);
        }
        
        // После миграции сообщений переносим согласия по всем гостевых идентификаторам
        // Это нужно на случай, если согласия были связаны с гостевой сессией
        try {
          const encryptionUtils = require('../utils/encryptionUtils');
          const encryptionKey = encryptionUtils.getEncryptionKey();
          
          // Получаем wallet адрес пользователя для обновления согласий
          const identityService = require('./identity-service');
          const walletIdentity = await identityService.findIdentity(userId, 'wallet');
          const normalizedWalletAddress = walletIdentity?.provider_id || null;
          
          for (const guestId of guestIdsToProcess) {
            // Ищем согласия по гостевому идентификатору в формате "guest_${guestId}"
            const guestWalletAddress = `guest_${guestId}`;
            
            const { rows: guestConsents } = await db.getQuery()(`
              SELECT id 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) {
              // Переносим согласия на пользователя и обновляем wallet_address
              await db.getQuery()(`
                UPDATE consent_logs
                SET user_id = $1,
                    wallet_address = COALESCE($2, wallet_address),
                    updated_at = NOW()
                WHERE id = ANY($3)
              `, [userId, normalizedWalletAddress, guestConsents.map(c => c.id)]);
              
              logger.info(`[SessionService] Перенесено ${guestConsents.length} согласий для guest ${guestId} → user ${userId}`);
            }
          }
        } catch (consentError) {
          // Не критично, если не удалось перенести согласия - просто логируем
          logger.warn(`[SessionService] Ошибка переноса согласий (не критично):`, consentError);
        }
      }

      return { success: true, processedCount: guestIdsToProcess.size };
    } catch (error) {
      logger.error(`[SessionService] Error linking guest messages:`, error);
      return { success: false, error: error.message };
    }
  }

  /**
   * Проверяет, был ли guest ID уже обработан
   * @param {string} guestId - ID гостя
   * @returns {Promise<boolean>} - Был ли guest ID обработан
   */
  async isGuestIdProcessed(guestId) {
    try {
      const encryptionUtils = require('../utils/encryptionUtils');
      const encryptionKey = encryptionUtils.getEncryptionKey();
      
      const result = await db.getQuery()(
        `SELECT * FROM unified_guest_mapping 
         WHERE decrypt_text(identifier_encrypted, $2) = $1 AND processed = true`,
        [`web:${guestId}`, encryptionKey]
      );
      
      return result.rows.length > 0;
    } catch (error) {
      logger.error(`[isGuestIdProcessed] Error checking guest ID ${guestId}:`, error);
      return false;
    }
  }

  // Обертка processGuestMessagesWrapper удалена - используется UniversalGuestService.migrateToUser

  /**
   * Получает сессию из хранилища по ID
   * @param {string} sessionId - ID сессии
   * @returns {Promise<Object|null>} - Объект сессии или null
   */
  async getSession(sessionId) {
    try {
      // Реализация будет зависеть от используемого хранилища сессий
      // Этот метод будет полезен, если нужно получить сессию не из текущего запроса
      return null; // Временная заглушка
    } catch (error) {
      logger.error(`[getSession] Error getting session ${sessionId}:`, error);
      return null;
    }
  }

  /**
   * Удаляет сессию
   * @param {Object} session - Объект сессии Express
   * @returns {Promise<boolean>} - Успешно ли удалена сессия
   */
  async destroySession(session) {
    try {
      return new Promise((resolve, reject) => {
        session.destroy((err) => {
          if (err) {
            logger.error('Error destroying session:', err);
            reject(err);
          } else {
            logger.info('Session destroyed successfully');
            resolve(true);
          }
        });
      });
    } catch (error) {
      logger.error(`[destroySession] Error:`, error);
      throw error;
    }
  }

  /**
   * Восстанавливает сессию из базы данных по ID
   * @param {string} sessionId - ID сессии
   * @returns {Promise<object|null>} - Данные сессии или null
   */
  async getSessionData(sessionId) {
    try {
      if (!sessionId) {
        logger.warn('[SessionService] Cannot restore session without sessionId');
        return null;
      }

      logger.info(`[SessionService] Attempting to retrieve session ${sessionId}`);

      const result = await encryptedDb.getData('session', { sid: sessionId });

      if (result.length === 0) {
        logger.info(`[SessionService] No session found with ID ${sessionId}`);
        return null;
      }

      const sessionData = result[0].sess;
      logger.info(`[SessionService] Retrieved session data for ${sessionId}`);

      return sessionData;
    } catch (error) {
      logger.error(`[SessionService] Error retrieving session ${sessionId}:`, error);
      return null;
    }
  }

  /**
   * Обновляет данные аутентификации в сессии
   * @param {object} session - Объект сессии
   * @param {object} authData - Данные аутентификации
   * @returns {Promise<boolean>} - Результат операции
   */
  async updateAuthData(session, authData) {
    try {
      if (!session || !authData) {
        logger.warn('[SessionService] Missing parameters for updateAuthData');
        return false;
      }

      const { userId, authType, userAccessLevel, ...otherData } = authData;

      if (!userId || !authType) {
        logger.warn('[SessionService] Missing userId or authType in authData');
        return false;
      }

      // Обновляем основные поля аутентификации
      session.userId = userId;
      session.authType = authType;
      session.authenticated = true;

      if (userAccessLevel !== undefined) {
        session.userAccessLevel = userAccessLevel;
      }

      // Обновляем дополнительные данные в зависимости от типа аутентификации
      if (authType === 'wallet' && otherData.address) {
        session.address = otherData.address.toLowerCase();
      } else if (authType === 'email' && otherData.email) {
        session.email = otherData.email.toLowerCase();
      } else if (authType === 'telegram') {
        if (otherData.telegramId) session.telegramId = otherData.telegramId;
        if (otherData.telegramUsername) session.telegramUsername = otherData.telegramUsername;
        if (otherData.telegramFirstName) session.telegramFirstName = otherData.telegramFirstName;
      }

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

      if (otherData.previousGuestId && !session.previousGuestId) {
        session.previousGuestId = otherData.previousGuestId;
      }

      // Сохраняем обновленную сессию
      return await this.saveSession(session, 'updateAuthData');
    } catch (error) {
      logger.error('[SessionService] Error updating auth data:', error);
      return false;
    }
  }

  /**
   * Очищает все массивы processedGuestIds из сессий в базе данных
   * @returns {Promise<boolean>} - Результат операции
   */
  async cleanupProcessedGuestIds() {
    try {
      logger.info('[SessionService] Starting cleanup of processedGuestIds from sessions');
      
      // Используем один SQL-запрос для обновления всех сессий
      const result = await encryptedDb.getData('session', { sess: { $regex: '.*"processedGuestIds":' } });
      
      for (const session of result) {
        const sessJson = JSON.parse(session.sess);
        delete sessJson.processedGuestIds;
        session.sess = JSON.stringify(sessJson);
        await encryptedDb.saveData('session', session);
      }
      
      logger.info(`[SessionService] Cleaned processedGuestIds from ${result.length} sessions`);
      return true;
    } catch (error) {
      logger.error('[SessionService] Error during cleanup:', error);
      return false;
    }
  }

  /**
   * Очищает данные аутентификации в сессии
   * @param {object} session - Объект сессии
   * @returns {Promise<boolean>} - Результат операции
   */
  async logout(session) {
    try {
      if (!session) {
        logger.warn('[SessionService] Cannot logout null session');
        return false;
      }

      // Сохраняем гостевые ID перед очисткой
      const guestId = session.guestId;

      // Удаляем данные аутентификации
      delete session.userId;
      delete session.authenticated;
      delete session.authType;
      delete session.userAccessLevel;
      delete session.address;
      delete session.email;
      delete session.telegramId;
      delete session.telegramUsername;
      delete session.telegramFirstName;

      // Очищаем массив processedGuestIds для экономии места
      if (session.processedGuestIds) {
        delete session.processedGuestIds;
      }

      // Восстанавливаем гостевой ID для продолжения работы
      if (guestId) {
        session.guestId = guestId;
      }

      // Сохраняем обновленную сессию
      return await this.saveSession(session, 'logout');
    } catch (error) {
      logger.error('[SessionService] Error during logout:', error);
      return false;
    }
  }
}

const sessionService = new SessionService();
module.exports = sessionService;
