"""
Модуль с абстрактным классом парсера.
"""

import os
from abc import ABC, abstractmethod
from typing import BinaryIO

from ..data_classes import ParsedDocument
from .file_types import FileType


class AbstractParser(ABC):
    """
    Абстрактный класс парсера документов.
    
    Все конкретные парсеры должны наследоваться от этого класса
    и реализовывать метод parse.
    """
    
    def __init__(self, file_types: FileType | list[FileType] | str | list[str] | None = None):
        """
        Инициализирует парсер.
        
        Args:
            file_types: Поддерживаемые типы файлов. Может быть одним из:
                - FileType - объект перечисления
                - list[FileType] - список объектов перечисления
                - str - строка с расширением файла (с точкой, например ".xml")
                - list[str] - список строк с расширениями
                - None - если не указан, парсер не ограничен типами
        """
        self.file_types = []
        
        if file_types is None:
            return
            
        # Преобразуем одиночный FileType в список
        if isinstance(file_types, FileType):
            self.file_types = [file_types]
        # Преобразуем список FileType в список
        elif isinstance(file_types, list) and all(isinstance(ft, FileType) for ft in file_types):
            self.file_types = file_types
        # Преобразуем строку расширения в FileType
        elif isinstance(file_types, str):
            try:
                self.file_types = [FileType.from_extension(file_types)]
            except ValueError:
                # Если не удалось найти подходящий FileType, создаем пустой список
                self.file_types = []
        # Преобразуем список строк расширений в список FileType
        elif isinstance(file_types, list) and all(isinstance(ft, str) for ft in file_types):
            self.file_types = []
            for ext in file_types:
                try:
                    self.file_types.append(FileType.from_extension(ext))
                except ValueError:
                    pass
    
    def _supported_extension(self, ext: str) -> bool:
        """
        Проверяет, поддерживается ли расширение файла.
        
        Этот метод должен быть переопределен в наследниках
        для указания поддерживаемых расширений.
        
        Args:
            ext (str): Расширение файла с точкой (.pdf, .docx и т.д.).
            
        Returns:
            bool: True, если расширение поддерживается, иначе False.
        """
        if not self.file_types:
            try:
                FileType.from_extension(ext)
                return True
            except ValueError:
                return False
        
        ext = ext.lower()
        for file_type in self.file_types:
            for supported_ext in file_type.value:
                if ext == supported_ext.lower():
                    return True
        return False
    
    def supports_file(self, file: str | BinaryIO | FileType) -> bool:
        """
        Проверяет, может ли парсер обработать файл.
        
        Args:
            file: Может быть одним из:
                - str: Путь к файлу
                - BinaryIO: Объект файла
                - FileType: Конкретный тип файла
            
        Returns:
            bool: True, если парсер поддерживает файл, иначе False.
        """
        # Если передан FileType, проверяем его наличие в списке поддерживаемых
        if isinstance(file, FileType):
            return file in self.file_types
        
        # Если переданы пустые file_types и не строка, не можем определить тип
        if not self.file_types and not isinstance(file, str):
            return False
            
        # Если передан путь к файлу, проверяем расширение
        if isinstance(file, str):
            _, ext = os.path.splitext(file)
            return self._supported_extension(ext)
            
        # Если передан бинарный объект, считаем что подходит
        # (конкретный тип будет проверен при вызове parse)
        return True
    
    @abstractmethod
    def parse(self, file: BinaryIO, file_type: FileType | None = None) -> ParsedDocument:
        """
        Парсит документ из объекта файла и возвращает его структурное представление.
        
        Args:
            file (BinaryIO): Объект файла для парсинга.
            file_type (FileType | None): Тип файла, если известен.
            
        Returns:
            ParsedDocument: Структурное представление документа.
        """
        pass
    
    @abstractmethod
    def parse_by_path(self, file_path: str) -> ParsedDocument:
        """
        Парсит документ по пути к файлу и возвращает его структурное представление.
        
        Args:
            file_path (str): Путь к файлу для парсинга.
            
        Returns:
            ParsedDocument: Структурное представление документа.
        """
        pass