const { spawn } = require('child_process');
const path = require('path');
const fs = require('fs');
const { ethers } = require('ethers');
const logger = require('../utils/logger');

/**
 * Сервис для управления DLE (Digital Legal Entity)
 */
class DLEService {
  /**
   * Создает новое DLE с заданными параметрами
   * @param {Object} dleParams - Параметры DLE
   * @returns {Promise<Object>} - Результат создания DLE
   */
  async createDLE(dleParams) {
    try {
      logger.info('Начало создания DLE с параметрами:', dleParams);

      // Валидация входных данных
      this.validateDLEParams(dleParams);

      // Подготовка параметров для деплоя
      const deployParams = this.prepareDeployParams(dleParams);

      // Сохраняем параметры во временный файл
      const paramsFile = this.saveParamsToFile(deployParams);

      // Копируем параметры во временный файл с предсказуемым именем
      const tempParamsFile = path.join(__dirname, '../scripts/deploy/current-params.json');
      logger.info(`Копирование параметров из ${paramsFile} в ${tempParamsFile}`);
      const deployDir = path.dirname(tempParamsFile);
      if (!fs.existsSync(deployDir)) {
        fs.mkdirSync(deployDir, { recursive: true });
      }
      fs.copyFileSync(paramsFile, tempParamsFile);
      logger.info(`Файл параметров скопирован успешно`);

      // Запускаем скрипт без передачи аргументов командной строки
      const result = await this.runDeployScript(paramsFile);

      logger.info('DLE успешно создано:', result);
      return result;
    } catch (error) {
      logger.error('Ошибка при создании DLE:', error);
      throw error;
    }
  }

  /**
   * Валидирует параметры DLE
   * @param {Object} params - Параметры DLE
   */
  validateDLEParams(params) {
    const requiredFields = [
      'name', 'symbol', 'location', 'isicCodes', 
      'partners', 'amounts', 'minTimelockDelay',
      'votingDelay', 'votingPeriod', 'proposalThreshold', 'quorumPercentage'
    ];

    for (const field of requiredFields) {
      if (params[field] === undefined) {
        throw new Error(`Отсутствует обязательный параметр: ${field}`);
      }
    }

    if (params.partners.length !== params.amounts.length) {
      throw new Error('Количество партнеров должно соответствовать количеству сумм распределения');
    }

    if (params.partners.length === 0) {
      throw new Error('Должен быть указан хотя бы один партнер');
    }

    if (params.quorumPercentage > 100) {
      throw new Error('Процент кворума не может превышать 100%');
    }
  }

  /**
   * Подготавливает параметры для деплоя
   * @param {Object} params - Параметры DLE
   * @returns {Object} - Подготовленные параметры
   */
  prepareDeployParams(params) {
    // Создаем копию объекта, чтобы не изменять исходный
    const deployParams = { ...params };

    // Преобразуем суммы из строк или чисел в BigNumber, если нужно
    deployParams.amounts = params.amounts.map(amount => {
      if (typeof amount === 'string' && !amount.startsWith('0x')) {
        return ethers.parseEther(amount).toString();
      }
      return amount.toString();
    });

    // Преобразуем proposalThreshold в BigNumber, если нужно
    if (typeof deployParams.proposalThreshold === 'string' && !deployParams.proposalThreshold.startsWith('0x')) {
      deployParams.proposalThreshold = ethers.parseEther(deployParams.proposalThreshold).toString();
    } else {
      deployParams.proposalThreshold = deployParams.proposalThreshold.toString();
    }

    return deployParams;
  }

  /**
   * Сохраняет параметры деплоя во временный файл
   * @param {Object} params - Параметры деплоя
   * @returns {string} - Путь к файлу с параметрами
   */
  saveParamsToFile(params) {
    const tempDir = path.join(__dirname, '../temp');
    
    if (!fs.existsSync(tempDir)) {
      fs.mkdirSync(tempDir, { recursive: true });
    }
    
    const fileName = `dle-params-${Date.now()}.json`;
    const filePath = path.join(tempDir, fileName);
    
    fs.writeFileSync(filePath, JSON.stringify(params, null, 2));
    
    return filePath;
  }

  /**
   * Запускает скрипт деплоя DLE
   * @param {string} paramsFile - Путь к файлу с параметрами
   * @returns {Promise<Object>} - Результат деплоя
   */
  runDeployScript(paramsFile) {
    return new Promise((resolve, reject) => {
      // Путь к новому скрипту для ручного деплоя (без фабрики)
      const scriptPath = path.join(__dirname, '../scripts/deploy/create-dle-manual.js');
      
      // Проверяем, существует ли скрипт
      if (!fs.existsSync(scriptPath)) {
        reject(new Error('Скрипт деплоя не найден: ' + scriptPath));
        return;
      }
      
      // Запускаем скрипт без передачи аргументов командной строки
      const hardhatProcess = spawn('npx', ['hardhat', 'run', scriptPath, '--network', 'sepolia'], {
        cwd: path.join(__dirname, '..'),
        env: { ...process.env },
        stdio: 'pipe'
      });

      let stdout = '';
      let stderr = '';

      hardhatProcess.stdout.on('data', (data) => {
        const output = data.toString();
        stdout += output;
        logger.debug(`[Hardhat] ${output}`);
      });

      hardhatProcess.stderr.on('data', (data) => {
        const output = data.toString();
        stderr += output;
        logger.error(`[Hardhat Error] ${output}`);
      });

      hardhatProcess.on('close', (code) => {
        if (code !== 0) {
          logger.error(`Скрипт деплоя завершился с кодом: ${code}`);
          reject(new Error(`Ошибка деплоя: ${stderr}`));
          return;
        }

        // Пытаемся извлечь адреса контрактов из вывода
        try {
          const tokenAddressMatch = stdout.match(/Адрес токена: (0x[a-fA-F0-9]{40})/);
          const timelockAddressMatch = stdout.match(/Адрес таймлока: (0x[a-fA-F0-9]{40})/);
          const governorAddressMatch = stdout.match(/Адрес контракта Governor: (0x[a-fA-F0-9]{40})/);

          if (tokenAddressMatch && timelockAddressMatch && governorAddressMatch) {
            resolve({
              tokenAddress: tokenAddressMatch[1],
              timelockAddress: timelockAddressMatch[1],
              governorAddress: governorAddressMatch[1],
              success: true
            });
          } else {
            // Если не удалось извлечь адреса, ищем файл с результатами
            const dlesDir = path.join(__dirname, '../contracts-data/dles');
            if (fs.existsSync(dlesDir)) {
              const files = fs.readdirSync(dlesDir);
              if (files.length > 0) {
                // Берем самый свежий файл
                const latestFile = files
                  .map(f => ({ name: f, time: fs.statSync(path.join(dlesDir, f)).mtime.getTime() }))
                  .sort((a, b) => b.time - a.time)[0].name;
                  
                const dleData = JSON.parse(fs.readFileSync(path.join(dlesDir, latestFile), 'utf8'));
                resolve({
                  ...dleData,
                  success: true
                });
              } else {
                reject(new Error('Не удалось найти информацию о созданном DLE'));
              }
            } else {
              reject(new Error('Не удалось найти директорию с результатами создания DLE'));
            }
          }
        } catch (error) {
          logger.error('Ошибка при извлечении результатов деплоя:', error);
          reject(error);
        }
      });
    });
  }

  /**
   * Получает список всех созданных DLE
   * @returns {Array<Object>} - Список DLE
   */
  getAllDLEs() {
    try {
      const dlesDir = path.join(__dirname, '../contracts-data/dles');
      
      if (!fs.existsSync(dlesDir)) {
        return [];
      }
      
      const files = fs.readdirSync(dlesDir);
      return files
        .filter(file => file.endsWith('.json') && file !== 'test.json' && file !== 'node-test.json')
        .map(file => {
          try {
            const data = JSON.parse(fs.readFileSync(path.join(dlesDir, file), 'utf8'));
            // Добавляем имя файла к данным DLE для возможности удаления пустых DLE
            return { ...data, _fileName: file };
          } catch (error) {
            logger.error(`Ошибка при чтении файла ${file}:`, error);
            // Для поврежденных файлов возвращаем минимальную информацию
            return { 
              _fileName: file,
              _corrupted: true
            };
          }
        });
    } catch (error) {
      logger.error('Ошибка при получении списка DLE:', error);
      throw error;
    }
  }
}

module.exports = new DLEService(); 