import logging

from bs4 import BeautifulSoup

from components.parser.abbreviations.abbreviation import Abbreviation
from components.parser.xml.constants import (ABBREVIATIONS,
                                             ABBREVIATIONS_PATTERNS,
                                             REGULATIONS, REGULATIONS_PATTERNS)
from components.parser.xml.structures import (ParsedRow, ParsedTable,
                                              ParsedTables)

logger = logging.getLogger(__name__)


class XMLTableParser:
    """
    Класс для парсинга таблиц из xml файлов.
    """

    def __init__(self, soup: BeautifulSoup):
        self.soup = soup
        self.abbreviations = []

    def parse(self) -> ParsedTables:
        """
        Парсинг таблиц из xml файла.

        Returns:
            ParsedTables - все таблицы, полученные из xml файла
        """
        tables = self.soup.find_all('w:tbl')
        logger.info(f"Found {len(tables)} tables in XML")

        parsed_tables = []
        self.abbreviations = []

        for table_ind, table in enumerate(tables):
            table_name = self._extract_table_name(table)

            type_short = self._classify_special_types(table_name, table)

            first_row = table.find('w:tr')
            columns_count = len(first_row.find_all('w:tc')) if first_row else 0

            parsed_table = self._parse_table(
                table=table,
                table_index=table_ind + 1,
                type_short=type_short,
                use_header=columns_count != 2,
                table_name=table_name,
            )

            parsed_tables.append(parsed_table)

            # Если таблица содержит сокращения, извлекаем их
            if type_short == ABBREVIATIONS:
                abbreviations_from_table = self._extract_abbreviations_from_table(
                    parsed_table
                )
                if abbreviations_from_table:
                    self.abbreviations.extend(abbreviations_from_table)

        logger.debug(f"Parsed {len(parsed_tables)} tables")

        # Создаем и нормализуем таблицы
        parsed_tables_obj = ParsedTables(tables=parsed_tables)
        normalized_tables = parsed_tables_obj.normalize()

        logger.debug(f"Normalized tables: {len(normalized_tables.tables)} main tables")

        if self.abbreviations:
            logger.debug(
                f"Extracted {len(self.abbreviations)} abbreviations from tables"
            )

        return normalized_tables

    def get_abbreviations(self) -> list[Abbreviation]:
        """
        Возвращает список аббревиатур, извлеченных из таблиц.

        Returns:
            list[Abbreviation]: Список аббревиатур
        """
        return self.abbreviations

    def _extract_abbreviations_from_table(
        self, table: ParsedTable
    ) -> list[Abbreviation]:
        """
        Извлечение аббревиатур из таблицы, помеченной как "сокращения".

        Args:
            table: ParsedTable - таблица сокращений

        Returns:
            list[Abbreviation]: Список аббревиатур
        """
        abbreviations = []

        # Проверяем, что таблица имеет нужный формат (обычно 2 колонки)
        for row in table.rows:
            if len(row.cols) >= 2:
                # Первая колонка обычно содержит сокращение, вторая - расшифровку
                short_form = row.cols[0].strip()
                full_form = row.cols[1].strip()

                # Создаем объект аббревиатуры только если оба поля не пусты
                if short_form and full_form:
                    abbreviation = Abbreviation(
                        short_form=short_form,
                        full_form=full_form,
                    )
                    # Обрабатываем аббревиатуру для определения типа и очистки
                    abbreviation.process()
                    abbreviations.append(abbreviation)

        return abbreviations

    @classmethod
    def _parse_table(
        cls,
        table: BeautifulSoup,
        table_index: int,
        type_short: str | None,
        use_header: bool = False,
        table_name: str | None = None,
    ) -> ParsedTable:
        """
        Парсинг таблицы.

        Args:
            table: BeautifulSoup - объект таблицы
            table_index: int - номер таблицы в xml-файле
            type_short: str | None - например, "сокращения" или "регламентирующие документы"
            use_header: bool - рассматривать ли первую строку таблицы как шапку таблицы
            table_name: str | None - название таблицы, если найдено

        Returns:
            ParsedTable - таблица, полученная из xml файла
        """
        parsed_rows = []
        header = [] if use_header else None
        
        rows = table.find_all('w:tr')

        for row_index, row in enumerate(rows):
            columns = row.find_all('w:tc')
            columns = [col.get_text() for col in columns]

            if (row_index == 0) and use_header:
                header = columns
            else:
                parsed_rows.append(ParsedRow(index=row_index, cols=columns))

        # Вычисляем статистические показатели таблицы
        rows_count = len(parsed_rows)
        
        # Определяем модальное количество столбцов
        if rows_count > 0:
            col_counts = [len(row.cols) for row in parsed_rows]
            from collections import Counter
            modal_cols_count = Counter(col_counts).most_common(1)[0][0]
        else:
            modal_cols_count = len(header) if header else 0

        # Инициализируем has_merged_cells как False, 
        # actual value will be determined in normalize method
        has_merged_cells = False

        return ParsedTable(
            index=table_index,
            short_type=type_short,
            header=header,
            rows=parsed_rows,
            name=table_name,
            rows_count=rows_count,
            modal_cols_count=modal_cols_count,
            has_merged_cells=has_merged_cells,
        )

    @staticmethod
    def _extract_columns_from_row(
        table_row: BeautifulSoup,
    ) -> list[str]:
        """
        Парсинг колонок из строки таблицы.

        Args:
            table_row: BeautifulSoup - объект строки таблицы

        Returns:
            list[str] - список колонок, полученных из строки таблицы
        """
        parsed_columns = []

        for cell in table_row.find_all('w:tc'):
            cell_text_parts = []

            for text_element in cell.find_all('w:t'):
                text_content = text_element.get_text()

            # Join all text parts from this cell and add to columns
            if cell_text_parts:
                parsed_columns.append(''.join(cell_text_parts))

        return parsed_columns

    @staticmethod
    def _classify_special_types(
        table_name: str | None,
        table: BeautifulSoup,
    ) -> str | None:
        """
        Поиск указаний на то, что таблица является специальной: "сокращения" или "регламентирующие документы".

        Args:
            table_name: str - название таблицы
            table: BeautifulSoup - объект таблицы

        Returns:
            str | None - либо "сокращения", либо "регламентирующие документы", либо None, если сокращения и регламенты не найдены
        """
        first_row = table.find('w:tr').text
        # Проверяем наличие шаблонов в тексте перед таблицей
        for pattern in ABBREVIATIONS_PATTERNS:
            if (table_name and pattern.lower() in table_name.lower()) or (
                pattern in first_row.lower()
            ):
                return ABBREVIATIONS

        for pattern in REGULATIONS_PATTERNS:
            if (table_name and pattern.lower() in table_name.lower()) or (
                pattern in first_row.lower()
            ):
                return REGULATIONS

        return None

    @staticmethod
    def _extract_table_name(
        table: BeautifulSoup,
    ) -> str | None:
        """
        Извлечение названия таблицы из текста перед таблицей.

        Метод ищет строки, содержащие типичные маркеры заголовков таблиц, такие как
        "Таблица", "Таблица N", "Табл.", и т.д., с учетом различных вариантов написания.

        Args:
            before_table_xml: str - блок xml-файла, предшествующий таблице

        Returns:
            str | None - название таблицы, если найдено, иначе None
        """
        # Создаем объект BeautifulSoup для парсинга XML фрагмента
        previous_paragraph = table.find_previous('w:p')

        if previous_paragraph:
            return previous_paragraph.get_text()

        return None