const csv = require('csv-parser');
const XLSX = require('xlsx');
const fs = require('fs');

/**
 * Процессор для обработки оборотно-сальдовой ведомости из 1С
 */
class BalanceSheetProcessor {
  constructor(pool) {
    this.pool = pool;
  }

  /**
   * Проверка, что строка похожа на данные (в колонке 0 текст, в 3 или 4 — число)
   */
  _isDataRow(rowArray) {
    if (!rowArray || rowArray.length < 5) return false;
    const firstCell = String(rowArray[0] || '').trim();
    const col3 = this.parseAmount(rowArray[3]);
    const col4 = this.parseAmount(rowArray[4]);
    return firstCell.length >= 1 && (col3 > 0 || col4 > 0);
  }

  /**
   * Парсинг CSV файла ведомости (UTF-8, с автоопределением начала данных и обработкой BOM)
   */
  async parseBalanceSheetCSV(filePath) {
    return new Promise((resolve, reject) => {
      const allRows = [];
      let currentRowIndex = 0;
      let dataStartRowIndex = -1;

      fs.createReadStream(filePath, { encoding: 'utf8' })
        .pipe(csv({ separator: ';', headers: false }))
        .on('data', (row) => {
          const rowArray = Object.values(row);
          // Убираем BOM с первой ячейки первой строки
          if (currentRowIndex === 0 && rowArray[0] !== undefined) {
            const first = String(rowArray[0]).replace(/^\uFEFF/, '');
            rowArray[0] = first;
          }
          allRows.push({ index: currentRowIndex, data: rowArray });
          currentRowIndex++;
        })
        .on('end', () => {
          // Автоопределение строки начала данных: ищем первую строку, где колонка 0 — текст, колонка 3 или 4 — число
          for (let i = 0; i < allRows.length; i++) {
            const rowArray = allRows[i].data;
            const firstCell = String(rowArray[0] || '').trim();
            if (firstCell.includes('Обороты за период') || firstCell.includes('Счет, Наименование')) {
              dataStartRowIndex = i + 1;
              break;
            }
            if (this._isDataRow(rowArray)) {
              dataStartRowIndex = i;
              break;
            }
          }
          if (dataStartRowIndex < 0) dataStartRowIndex = 9;
          const rows = allRows.filter(r => r.index >= dataStartRowIndex);
          console.log(`[BalanceSheetProcessor] Начало данных с строки ${dataStartRowIndex}, строк данных: ${rows.length}`);
          resolve(rows);
        })
        .on('error', reject);
    });
  }

  /**
   * Парсинг XLSX файла ведомости
   */
  async parseBalanceSheetXLSX(filePath) {
    try {
      const workbook = XLSX.readFile(filePath);
      const sheetName = workbook.SheetNames[0];
      const worksheet = workbook.Sheets[sheetName];
      
      // Конвертируем в массив массивов
      const rawData = XLSX.utils.sheet_to_json(worksheet, { 
        header: 1, 
        defval: '',
        raw: false
      });

      // Пропускаем метаданные (первые 9 строк)
      const rows = [];
      for (let i = 9; i < rawData.length; i++) {
        rows.push({
          index: i,
          data: rawData[i] || []
        });
      }

      return rows;
    } catch (error) {
      throw new Error(`Ошибка чтения XLSX файла: ${error.message}`);
    }
  }

  /**
   * Определение адреса дома из строки
   */
  isBuildingAddress(rowData) {
    if (!rowData || rowData.length === 0) return false;
    
    const firstCell = String(rowData[0] || '').trim();
    
    // Пропускаем служебные строки
    if (firstCell.startsWith('<...>') || 
        firstCell === '' ||
        firstCell.includes('Оборотно-сальдовая') ||
        firstCell.includes('Выводимые данные') ||
        firstCell.includes('Счет, Наименование') ||
        firstCell.includes('Подразделение') ||
        firstCell.includes('Номенклатурные группы') ||
        firstCell.includes('Статьи затрат')) {
      return false;
    }
    
    // Не является счетом (20, 20.01, 20.01.01)
    if (/^\d+\.?\d*\.?\d*$/.test(firstCell)) return false;
    
    // Не является статьей затрат (содержит типичные слова статей без адреса)
    const expenseKeywords = [
      'Административно-управленческие расходы',
      'Текущее обслуживание общедомового имущества',
      'Ремонт кровли',
      'Благоустройство территории',
      'Услуги специализированных организаций',
      'Техническое обслуживание',
      'Расходные материалы',
      'Механизированная уборка',
      'Хозяйственные расходы',
      'Резервный фонд',
      'Судебные издержки',
      'Обслуживание инженерного оборудования'
    ];
    
    // Если строка содержит только ключевые слова статей без адреса - это не адрес
    const isOnlyExpenseKeyword = expenseKeywords.some(keyword => 
      firstCell === keyword || firstCell.startsWith(keyword + ';')
    );
    if (isOnlyExpenseKeyword) return false;
    
    // Адрес должен содержать буквы (название улицы/участка)
    const hasLetters = /[А-Яа-я]/.test(firstCell);
    
    // Проверяем наличие сумм в строке (колонки 3-6: обороты и сальдо)
    // Если есть большие суммы - это может быть адрес участка/комплекса
    const hasAmounts = rowData.length > 3 && (
      this.parseAmount(rowData[3]) > 0 || // Дебет обороты
      this.parseAmount(rowData[4]) > 0 || // Кредит обороты
      this.parseAmount(rowData[5]) > 0 || // Дебет сальдо
      this.parseAmount(rowData[6]) > 0    // Кредит сальдо
    );
    
    // Адрес с номером дома (стандартный случай)
    const hasNumber = /\d/.test(firstCell);
    
    // Паттерны адресов
    const addressPatterns = [
      /\d+\/\d+/,                  // "6/1", "152/4" - номер дома с корпусом
      /^[А-Яа-я]+\s+[А-Яа-я]+.*\d/, // "Булата Имашева 6/1"
      /ул\.|пер\.|пр\.|проспект.*\d/i, // "ул. Ленина, д.12"
      /д\.\s*\d/i,                  // "д. 12"
      /^[А-Яа-я]+.*\d+[\/\-]?\d*/,  // Общий паттерн: буквы + цифры
      /^[А-Яа-я]+\s+[А-Яа-я]+$/     // Название участка/комплекса без номера (например, "Зеленая Роща")
    ];
    
    const hasAddressPattern = addressPatterns.some(pattern => pattern.test(firstCell));
    
    // Адрес обычно не слишком длинный (до 100 символов) и не слишком короткий (больше 3)
    const validLength = firstCell.length >= 4 && firstCell.length < 100;
    
    // Если есть паттерн адреса, буквы и валидная длина - это адрес
    // Дополнительно: если есть суммы в строке и это похоже на название участка (2 слова без цифр) - тоже адрес
    const isDistrictName = /^[А-Яа-я]+\s+[А-Яа-я]+$/.test(firstCell) && hasAmounts;
    
    return hasAddressPattern && hasLetters && validLength && (hasNumber || isDistrictName);
  }

  /**
   * Парсинг ведомости и группировка по домам
   */
  async parseBalanceSheet(filePath, fileType) {
    let rows;
    
    if (fileType === 'CSV') {
      rows = await this.parseBalanceSheetCSV(filePath);
    } else if (fileType === 'XLSX') {
      rows = await this.parseBalanceSheetXLSX(filePath);
    } else {
      throw new Error(`Неподдерживаемый тип файла: ${fileType}`);
    }

    const buildings = {};
    let currentBuilding = null;
    let currentGroup = null;
    let currentArticle = null;

    console.log(`[BalanceSheetProcessor] Начало парсинга, строк в файле: ${rows.length}`);

    for (const row of rows) {
      const rowData = row.data;
      if (!rowData || rowData.length === 0) continue;

      const firstCell = String(rowData[0] || '').trim();
      const debitTurnover = this.parseAmount(rowData[3]); // Обороты за период - Дебет
      const creditTurnover = this.parseAmount(rowData[4]); // Обороты за период - Кредит
      const endDebit = this.parseAmount(rowData[5]); // Сальдо на конец периода - Дебет
      const endCredit = this.parseAmount(rowData[6]); // Сальдо на конец периода - Кредит

      // Пропускаем пустые строки
      if (!firstCell || firstCell === '') continue;

      // Определяем адрес дома
      if (this.isBuildingAddress(rowData)) {
        // Если это тот же адрес, что и текущий - не сбрасываем группу (может быть повтор)
        if (currentBuilding !== firstCell) {
          currentBuilding = firstCell;
          currentGroup = null;
          currentArticle = null;
          console.log(`[BalanceSheetProcessor] Найден адрес дома: "${currentBuilding}"`);
        }
        
        if (!buildings[currentBuilding]) {
          buildings[currentBuilding] = {
            address: currentBuilding,
            groups: {},
            totalExpenses: 0,
            totalIncome: 0,
            balance: 0
          };
        }
        // Учитываем сумму в строке адреса, чтобы расход по дому не был нулевым при отсутствии статей ниже
        if (debitTurnover > 0) {
          buildings[currentBuilding].totalExpenses += debitTurnover;
          if (!buildings[currentBuilding].groups['Итого по дому']) {
            buildings[currentBuilding].groups['Итого по дому'] = {
              name: 'Итого по дому',
              articles: {},
              total: 0
            };
            buildings[currentBuilding].groups['Итого по дому'].articles[currentBuilding] = {
              name: currentBuilding,
              amount: 0,
              details: []
            };
          }
          buildings[currentBuilding].groups['Итого по дому'].articles[currentBuilding].amount += debitTurnover;
          buildings[currentBuilding].groups['Итого по дому'].total += debitTurnover;
        }
        continue;
      }

      // Если дом не определен, пропускаем строку
      if (!currentBuilding) continue;

      // Определяем номенклатурную группу: известные названия (точное/частичное) или произвольное название из ОСВ
      // Список типичных групп — у разных УК формулировки могут отличаться, поэтому проверяем и по вхождению
      const groupKeywords = [
        'Административно-управленческие расходы',
        'Судебные издержки',
        'Услуги специализированных организаций',
        'Благоустройство территории',
        'Техническое обслуживание конструктивных элементов',
        'Обслуживание инженерного оборудования',
        'Расходные материалы',
        'Текущее обслуживание общедомового имущества',
        'Механизированная уборка',
        'Хозяйственные расходы',
        'Резервный фонд'
      ];
      const firstCellLower = firstCell.toLowerCase();
      const matchedGroup = groupKeywords.find(keyword => 
        firstCell === keyword || 
        firstCell.startsWith(keyword + ';') || 
        firstCell.startsWith(keyword) ||
        firstCellLower.includes(keyword.toLowerCase())
      );
      
      if (matchedGroup) {
        currentGroup = matchedGroup;
        currentArticle = null;
        
        if (!buildings[currentBuilding].groups[currentGroup]) {
          buildings[currentBuilding].groups[currentGroup] = {
            name: currentGroup,
            articles: {},
            total: 0
          };
        }
        continue;
      }

      // Динамическая группа: если есть сумма и строка не служебная — считаем название группы/услуги из ОСВ (у разных компаний названия разные)
      if (currentBuilding && debitTurnover > 0 && !firstCell.startsWith('<...>') && firstCell.length >= 2) {
        const dynamicGroupName = firstCell.trim();
        if (!buildings[currentBuilding].groups[dynamicGroupName]) {
          buildings[currentBuilding].groups[dynamicGroupName] = {
            name: dynamicGroupName,
            articles: {},
            total: 0
          };
        }
        currentGroup = dynamicGroupName;
        currentArticle = firstCell;
        if (!buildings[currentBuilding].groups[currentGroup].articles[currentArticle]) {
          buildings[currentBuilding].groups[currentGroup].articles[currentArticle] = {
            name: currentArticle,
            amount: 0,
            details: []
          };
        }
        buildings[currentBuilding].groups[currentGroup].articles[currentArticle].amount += debitTurnover;
        buildings[currentBuilding].groups[currentGroup].total += debitTurnover;
        buildings[currentBuilding].totalExpenses += debitTurnover;
        if (rowData.length > 1) {
          const secondCell = String(rowData[1] || '').trim();
          if (secondCell && secondCell !== '' && !secondCell.match(/^\d+[\s,\.]*\d*$/)) {
            buildings[currentBuilding].groups[currentGroup].articles[currentArticle].details.push({
              description: secondCell,
              amount: debitTurnover
            });
          }
        }
        continue;
      }

      // Определяем статью затрат (если есть отступ или вложенность)
      if (currentGroup && debitTurnover > 0) {
        // Пропускаем строки с "<...>" - это служебные строки
        if (firstCell.startsWith('<...>')) {
          continue;
        }
        
        // Это статья затрат
        if (!currentArticle || firstCell !== currentArticle) {
          currentArticle = firstCell;
          
          if (!buildings[currentBuilding].groups[currentGroup].articles[currentArticle]) {
            buildings[currentBuilding].groups[currentGroup].articles[currentArticle] = {
              name: currentArticle,
              amount: 0,
              details: []
            };
          }
        }
        
        // Добавляем сумму
        buildings[currentBuilding].groups[currentGroup].articles[currentArticle].amount += debitTurnover;
        buildings[currentBuilding].groups[currentGroup].total += debitTurnover;
        buildings[currentBuilding].totalExpenses += debitTurnover;
        
        // Сохраняем детализацию если есть (вторая колонка может содержать описание)
        if (rowData.length > 1) {
          const secondCell = String(rowData[1] || '').trim();
          if (secondCell && secondCell !== '' && !secondCell.match(/^\d+[\s,\.]*\d*$/)) {
            // Это не число, значит описание
            buildings[currentBuilding].groups[currentGroup].articles[currentArticle].details.push({
              description: secondCell,
              amount: debitTurnover
            });
          }
        }
      }
    }

    const buildingCount = Object.keys(buildings).length;
    console.log(`[BalanceSheetProcessor] Найдено домов: ${buildingCount}`);
    if (buildingCount > 0) {
      console.log(`[BalanceSheetProcessor] Адреса домов:`, Object.keys(buildings).slice(0, 5).join(', '), buildingCount > 5 ? '...' : '');
      for (const [addr, data] of Object.entries(buildings)) {
        const articleCount = Object.values(data.groups || {}).reduce((s, g) => s + Object.keys(g.articles || {}).length, 0);
        console.log(`[BalanceSheetProcessor] Дом "${addr}": totalExpenses=${data.totalExpenses}, статей в expenses_by_items: ${articleCount}`);
      }
    } else {
      console.warn(`[BalanceSheetProcessor] ⚠ Дома не найдены в ведомости!`);
    }

    return buildings;
  }

  /**
   * Парсинг суммы из строки (удаление пробелов, запятых)
   */
  parseAmount(value) {
    if (!value) return 0;
    const str = String(value).replace(/\s/g, '').replace(',', '.');
    const num = parseFloat(str);
    return isNaN(num) ? 0 : num;
  }

  /**
   * Преобразование данных ведомости в формат для БД
   */
  convertToBuildingFinancialData(buildings, reportId, periodStart, periodEnd) {
    const result = [];

    for (const [address, buildingData] of Object.entries(buildings)) {
      // Формируем expenses_by_items
      const expensesByItems = {};
      
      for (const [groupName, group] of Object.entries(buildingData.groups)) {
        for (const [articleName, article] of Object.entries(group.articles)) {
          const key = `${groupName} > ${articleName}`;
          expensesByItems[key] = article.amount;
        }
      }

      result.push({
        address: address,
        totalIncome: buildingData.totalIncome || 0,
        totalExpenses: buildingData.totalExpenses || 0,
        balance: buildingData.balance || (buildingData.totalIncome - buildingData.totalExpenses),
        expensesByItems: expensesByItems,
        periodStart: periodStart,
        periodEnd: periodEnd,
        periodType: this.determinePeriodType(periodStart, periodEnd),
        metadata: {
          groups: buildingData.groups,
          rawData: buildingData
        }
      });
    }

    return result;
  }

  /**
   * Определение типа периода
   */
  determinePeriodType(start, end) {
    const startDate = new Date(start);
    const endDate = new Date(end);
    const diffMonths = (endDate.getFullYear() - startDate.getFullYear()) * 12 + 
                       (endDate.getMonth() - startDate.getMonth());
    
    if (diffMonths === 0) return 'month';
    if (diffMonths <= 3) return 'quarter';
    if (diffMonths <= 12) return 'year';
    return 'year';
  }

  /**
   * Извлечение периода из названия файла или метаданных
   */
  extractPeriodFromFilename(filename) {
    // Пытаемся найти год в названии файла
    const yearMatch = filename.match(/20\d{2}/);
    const year = yearMatch ? parseInt(yearMatch[0]) : new Date().getFullYear();
    
    // Пытаемся найти месяц в названии файла (например, "за_2025_г" или "01.2025")
    const monthMatch = filename.match(/(\d{1,2})[\.\/](\d{4})|(\d{4})[\.\/](\d{1,2})/);
    
    if (monthMatch) {
      // Найден месяц и год
      let month, yearFromMatch;
      if (monthMatch[1] && monthMatch[2]) {
        month = parseInt(monthMatch[1]);
        yearFromMatch = parseInt(monthMatch[2]);
      } else if (monthMatch[3] && monthMatch[4]) {
        yearFromMatch = parseInt(monthMatch[3]);
        month = parseInt(monthMatch[4]);
      }
      
      if (month && yearFromMatch) {
        const startDate = new Date(yearFromMatch, month - 1, 1);
        const endDate = new Date(yearFromMatch, month, 0); // Последний день месяца
        
        return {
          start: `${yearFromMatch}-${String(month).padStart(2, '0')}-01`,
          end: `${yearFromMatch}-${String(month).padStart(2, '0')}-${String(endDate.getDate()).padStart(2, '0')}`,
          type: 'month'
        };
      }
    }
    
    // Если месяц не найден, используем весь год
    return {
      start: `${year}-01-01`,
      end: `${year}-12-31`,
      type: 'year'
    };
  }
}

module.exports = BalanceSheetProcessor;
