/**
 * 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 fs = require('fs').promises;
const path = require('path');
require('dotenv').config();
const { getPool } = require('../db');
const pool = getPool();
const logger = require('../utils/logger');

// Читаем ключ шифрования из файла
async function getEncryptionKey() {
  try {
    const keyPath = '/app/ssl/keys/full_db_encryption.key';
    const key = await fs.readFile(keyPath, 'utf8');
    return key.trim();
  } catch (error) {
    logger.error('Ошибка чтения ключа шифрования:', error);
    throw new Error('Не удалось прочитать ключ шифрования');
  }
}

async function runMigrations() {
  try {
    console.log('Запуск миграций...');

    // Читаем ключ шифрования
    const encryptionKey = await getEncryptionKey();
    console.log('Ключ шифрования загружен');

    // Создаем таблицу для отслеживания миграций, если её нет
    await pool.query(`
      CREATE TABLE IF NOT EXISTS migrations (
        id SERIAL PRIMARY KEY,
        name VARCHAR(255) NOT NULL,
        executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
      )
    `);

    // Получаем список выполненных миграций
    const { rows } = await pool.query('SELECT name FROM migrations');
    const executedMigrations = new Set(rows.map((row) => row.name));

    // Читаем файлы миграций
    const migrationsDir = path.join(__dirname, '../db/migrations');
    const files = await fs.readdir(migrationsDir);

    // Сортируем файлы по номеру
    const migrationFiles = files
      .filter((f) => f.endsWith('.sql'))
      .sort((a, b) => {
        const numA = parseInt(a.split('_')[0]);
        const numB = parseInt(b.split('_')[0]);
        return numA - numB;
      });

    // Выполняем миграции
    for (const file of migrationFiles) {
      if (!executedMigrations.has(file)) {
        const filePath = path.join(migrationsDir, file);
        const fileContent = await fs.readFile(filePath, 'utf-8');

        // Ищем начало UP секции (или начало файла)
        const upMarker = '-- UP Migration';
        const downMarker = '-- DOWN Migration';
        let upSqlStartIndex = fileContent.indexOf(upMarker);
        if (upSqlStartIndex !== -1) {
          // Ищем перевод строки после маркера
          let newlineIndex = fileContent.indexOf('\n', upSqlStartIndex);
          if (newlineIndex === -1) { // Если маркер в последней строке
             newlineIndex = fileContent.length;
          }
          upSqlStartIndex = newlineIndex + 1; // Начинаем со следующей строки
        } else {
          upSqlStartIndex = 0; // Если маркера нет, берем все с начала
        }

        // Ищем конец UP секции (начало DOWN секции)
        let upSqlEndIndex = fileContent.indexOf(downMarker);
        if (upSqlEndIndex === -1) {
          upSqlEndIndex = fileContent.length; // Если маркера DOWN нет, берем все до конца
        }

        // Извлекаем только UP SQL
        let sqlToExecute = fileContent.substring(upSqlStartIndex, upSqlEndIndex).trim();

        if (!sqlToExecute) {
          logger.warn(`Migration file ${file} has no executable UP SQL content. Skipping.`);
          continue; // Пропускаем пустые миграции
        }

        logger.info(`Executing UP migration from ${file}...`);
        
        // Выделяем CREATE EXTENSION команды, которые должны выполняться вне транзакции
        const extensionRegex = /CREATE\s+EXTENSION\s+IF\s+NOT\s+EXISTS\s+\w+;?/gi;
        const extensionCommands = [];
        sqlToExecute = sqlToExecute.replace(extensionRegex, (match) => {
          extensionCommands.push(match.replace(/;?\s*$/, ''));
          return ''; // Удаляем из основного SQL
        });

        // Выполняем CREATE EXTENSION команды вне транзакции
        for (const extCmd of extensionCommands) {
          try {
            await pool.query(extCmd);
            logger.info(`Extension command executed: ${extCmd}`);
          } catch (error) {
            // Игнорируем ошибку, если расширение уже установлено
            if (!error.message.includes('already exists')) {
              logger.warn(`Warning executing extension command: ${error.message}`);
            }
          }
        }

        // Очищаем оставшийся SQL от пустых строк
        sqlToExecute = sqlToExecute.trim();

        // Если после удаления CREATE EXTENSION остался только пустой SQL, пропускаем транзакцию
        if (!sqlToExecute) {
          await pool.query('INSERT INTO migrations (name) VALUES ($1)', [file]);
          logger.info(`Migration ${file} executed successfully (extension only)`);
          continue;
        }

        await pool.query('BEGIN');
        try {
          // Создаем функцию для получения ключа шифрования
          await pool.query(`
            CREATE OR REPLACE FUNCTION get_encryption_key()
            RETURNS TEXT AS $$
            BEGIN
              RETURN '${encryptionKey}';
            END;
            $$ LANGUAGE plpgsql;
          `);
          
          // Выполняем только извлеченный UP SQL
          await pool.query(sqlToExecute);
          await pool.query('INSERT INTO migrations (name) VALUES ($1)', [file]);
          await pool.query('COMMIT');
          logger.info(`Migration ${file} executed successfully`);
        } catch (error) {
          await pool.query('ROLLBACK');
          logger.error(`Error executing migration ${file}:`, error); // Логируем ошибку перед пробросом
          throw error;
        }
      }
    }

    // Выполняем SQL-функции
    const functionsDir = path.join(migrationsDir, 'functions');
    if (
      await fs
        .stat(functionsDir)
        .then(() => true)
        .catch(() => false)
    ) {
      const functionFiles = await fs.readdir(functionsDir);

      for (const file of functionFiles) {
        if (file.endsWith('.sql')) {
          const filePath = path.join(functionsDir, file);
          const sql = await fs.readFile(filePath, 'utf-8');

          try {
            await pool.query(sql);
            logger.info(`Function ${file} executed successfully`);
          } catch (error) {
            logger.error(`Error executing function ${file}:`, error);
            throw error;
          }
        }
      }
    }

    console.log('Все миграции успешно применены');
  } catch (error) {
    console.error('Ошибка при выполнении миграций:', error);
    process.exit(1);
  } finally {
    await pool.end();
  }
}

runMigrations();
