"""
FileDataReader: Facade para acceder a funcionalidades de DataFrame desde un File.

Proporciona una interfaz limpia para convertir archivos a DataFrames sin necesidad
de pasar manualmente los bytes del archivo a los readers.
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Optional

from tai_alphi import Alphi

from .readers import CSVReader, ExcelReader, ParquetReader, JSONReader

if TYPE_CHECKING:
    from ..base.file import File

try:
    import pandas as pd
except ImportError:
    raise ImportError(
        "El módulo 'data' requiere pandas. "
        "Instala con: pip install tai-storage[data]"
    )

logger = Alphi.get_logger_by_name('tai-storage')


class FileDataReader:
    """
    Facade para leer archivos como DataFrames.
    
    Esta clase proporciona una API conveniente para convertir archivos
    a pandas DataFrames, detectando automáticamente el formato basándose
    en la extensión del archivo.
    
    No necesitas pasar manualmente los bytes del archivo, se obtienen
    automáticamente del objeto File.
    
    Examples:
        >>> # Obtener el reader del archivo
        >>> file = folder.get_file('datos.csv')
        >>> data = file.get_data()
        >>> 
        >>> # Auto-detectar formato
        >>> df = data.to_dataframe(sep=';', infer_types=True)
        >>> 
        >>> # Usar métodos específicos
        >>> df = data.to_csv(sep=';', encoding='latin1')
        >>> df = data.to_excel(sheet_name='Ventas')
    """
    
    def __init__(self, file: File):
        """
        Inicializa el FileDataReader.
        
        Args:
            file: Instancia de File a leer
        """
        self.file = file
        self._bytes_cache: Optional[bytes] = None
    
    def _get_bytes(self, use_cache: bool = True) -> bytes:
        """
        Obtiene los bytes del archivo.
        
        Args:
            use_cache: Si True, usa cache si está disponible
            
        Returns:
            Bytes del archivo
        """
        if self._bytes_cache is None or not use_cache:
            self._bytes_cache = self.file.read()
        return self._bytes_cache
    
    def to_dataframe(
        self,
        # Argumentos comunes
        infer_types: bool = True,
        with_fields: Optional[list[str]] = None,
        normalize_columns: bool = False,
        column_names: Optional[list[str]] = None,
        use_cache: bool = True,
        
        # Excel específicos
        sheet_name: Optional[str] = None,
        entrypoint: Optional[str] = None,
        width: Optional[int] = None,
        height: Optional[int] = None,
        regex: bool = False,
        infer_dimensions: bool = False,
        
        # CSV específicos
        sep: str = ",",
        encoding: str = 'utf-8',
        
        # JSON específicos
        orient: str = 'records',
        
        **kwargs
    ) -> pd.DataFrame:
        """
        Convierte el archivo a pandas DataFrame automáticamente según su extensión.
        
        Formatos soportados:
        - .csv: Archivos CSV
        - .xlsx, .xls: Archivos Excel
        - .parquet: Archivos Parquet
        - .json: Archivos JSON
        
        Args:
            infer_types: Si True, infiere y convierte tipos automáticamente
            with_fields: Lista de columnas a incluir (None = todas)
            normalize_columns: Si True, normaliza nombres de columnas a snake_case
            column_names: Nombres personalizados para las columnas (CSV/Excel)
            use_cache: Si True, usa bytes en cache si están disponibles
            
            # Argumentos específicos de Excel:
            sheet_name: Nombre de la hoja (por defecto: primera hoja o 'Hoja1')
            entrypoint: Celda o texto donde empiezan los datos (ej: 'A5', 'Código')
            width: Ancho del rango de datos
            height: Alto del rango de datos
            regex: Si True, trata entrypoint como regex
            infer_dimensions: Si True, infiere automáticamente alto y ancho
            
            # Argumentos específicos de CSV:
            sep: Separador de campos (default: ',')
            encoding: Codificación del archivo (default: 'utf-8')
            
            # Argumentos específicos de JSON:
            orient: Orientación del JSON ('records', 'index', 'columns', etc.)
            
            **kwargs: Argumentos adicionales para el reader específico
            
        Returns:
            DataFrame con los datos del archivo
            
        Raises:
            ValueError: Si el formato no es soportado
            
        Examples:
            >>> # CSV simple
            >>> df = file.get_data().to_dataframe()
            
            >>> # CSV con separador personalizado
            >>> df = file.get_data().to_dataframe(sep=';', encoding='latin1')
            
            >>> # Excel con hoja específica
            >>> df = file.get_data().to_dataframe(sheet_name='Ventas')
            
            >>> # Excel con punto de entrada y nombres de columnas personalizados
            >>> data = file.get_data()
            >>> df = data.to_dataframe(
            ...     sheet_name='Datos',
            ...     entrypoint='Código',
            ...     infer_dimensions=True,
            ...     column_names=['codigo', 'nombre', 'precio']
            ... )
        """
        logger.debug(f"Converting {self.file.filename} to DataFrame")
        
        # Obtener bytes del archivo
        file_bytes = self._get_bytes(use_cache=use_cache)
        
        # Determinar el reader según la extensión
        extension = self.file.extension.lower()
        
        if extension in ['.xlsx', '.xls']:
            reader = ExcelReader()
            
            # Usar nombre de hoja por defecto si no se especifica
            if sheet_name is None:
                sheet_name = 'Hoja1'
            
            df = reader.read(
                file_bytes=file_bytes,
                sheet_name=sheet_name,
                column_names=column_names,
                entrypoint=entrypoint,
                width=width,
                height=height,
                regex=regex,
                infer_dimensions=infer_dimensions,
                normalize_columns=normalize_columns,
                infer_types=infer_types,
                with_fields=with_fields,
                **kwargs
            )
        
        elif extension == '.csv':
            reader = CSVReader()
            df = reader.read(
                file_bytes=file_bytes,
                sep=sep,
                encoding=encoding,
                column_names=column_names,
                normalize_columns=normalize_columns,
                infer_types=infer_types,
                with_fields=with_fields,
                **kwargs
            )
        
        elif extension == '.parquet':
            reader = ParquetReader()
            df = reader.read(
                file_bytes=file_bytes,
                **kwargs
            )
            
            # Aplicar filtrado de campos si se especifica
            if with_fields:
                df = df[[col for col in with_fields if col in df.columns]]
        
        elif extension == '.json':
            reader = JSONReader()
            df = reader.read(
                file_bytes=file_bytes,
                orient=orient,
                infer_types=infer_types,
                **kwargs
            )
            
            # Aplicar filtrado de campos si se especifica
            if with_fields:
                df = df[[col for col in with_fields if col in df.columns]]
        
        else:
            raise ValueError(
                f"Formato no soportado: {extension}. "
                f"Formatos soportados: .csv, .xlsx, .xls, .parquet, .json"
            )
        
        logger.info(
            f"DataFrame created from {self.file.filename}: "
            f"{len(df)} rows × {len(df.columns)} columns"
        )
        
        return df
    
    def to_dataframe_from_csv(
        self,
        sep: str = ",",
        encoding: str = 'utf-8',
        column_names: Optional[list[str]] = None,
        normalize_columns: bool = False,
        infer_types: bool = True,
        with_fields: Optional[list[str]] = None,
        use_cache: bool = True,
        **kwargs
    ) -> pd.DataFrame:
        """
        Convierte el archivo CSV a DataFrame.
        
        Método de conveniencia con todos los parámetros disponibles para CSV.
        
        Args:
            sep: Separador de campos
            encoding: Codificación del archivo
            column_names: Nombres personalizados para columnas
            normalize_columns: Si True, normaliza nombres a snake_case
            infer_types: Si True, infiere tipos automáticamente
            with_fields: Columnas a incluir
            use_cache: Si True, usa bytes en cache si están disponibles
            **kwargs: Argumentos adicionales
            
        Returns:
            DataFrame con los datos
            
        Examples:
            >>> df = file.get_data().to_csv(sep=';', encoding='latin1')
            >>> df = file.get_data().to_csv(
            ...     normalize_columns=True,
            ...     with_fields=['id', 'nombre']
            ... )
        """
        reader = CSVReader()
        return reader.read(
            file_bytes=self._get_bytes(use_cache=use_cache),
            sep=sep,
            encoding=encoding,
            column_names=column_names,
            normalize_columns=normalize_columns,
            infer_types=infer_types,
            with_fields=with_fields,
            **kwargs
        )
    
    def to_dataframe_from_excel(
        self,
        sheet_name: str = 'Hoja1',
        column_names: Optional[list[str]] = None,
        entrypoint: Optional[str] = None,
        width: Optional[int] = None,
        height: Optional[int] = None,
        regex: bool = False,
        infer_dimensions: bool = False,
        normalize_columns: bool = False,
        infer_types: bool = True,
        with_fields: Optional[list[str]] = None,
        use_cache: bool = True,
        **kwargs
    ) -> pd.DataFrame:
        """
        Convierte el archivo Excel a DataFrame.
        
        Método de conveniencia con todos los parámetros disponibles para Excel.
        
        Args:
            sheet_name: Nombre de la hoja
            column_names: Nombres personalizados para columnas
            entrypoint: Celda o texto donde empiezan los datos
            width: Ancho del rango
            height: Alto del rango
            regex: Si True, trata entrypoint como regex
            infer_dimensions: Si True, infiere alto y ancho automáticamente
            normalize_columns: Si True, normaliza nombres a snake_case
            infer_types: Si True, infiere tipos automáticamente
            with_fields: Columnas a incluir
            use_cache: Si True, usa bytes en cache si están disponibles
            **kwargs: Argumentos adicionales
            
        Returns:
            DataFrame con los datos
            
        Examples:
            >>> df = file.get_data().to_excel(sheet_name='Ventas')
            >>> df = file.get_data().to_excel(
            ...     entrypoint='Código',
            ...     infer_dimensions=True
            ... )
        """
        reader = ExcelReader()
        return reader.read(
            file_bytes=self._get_bytes(use_cache=use_cache),
            sheet_name=sheet_name,
            column_names=column_names,
            entrypoint=entrypoint,
            width=width,
            height=height,
            regex=regex,
            infer_height=infer_dimensions,
            infer_width=infer_dimensions,
            normalize_columns=normalize_columns,
            infer_types=infer_types,
            with_fields=with_fields,
            **kwargs
        )
    
    def to_dataframe_from_parquet(
        self,
        use_cache: bool = True,
        **kwargs
    ) -> pd.DataFrame:
        """
        Convierte el archivo Parquet a DataFrame.
        
        Args:
            use_cache: Si True, usa bytes en cache si están disponibles
            **kwargs: Argumentos adicionales para pd.read_parquet
            
        Returns:
            DataFrame con los datos
            
        Examples:
            >>> df = file.get_data().to_parquet()
        """
        reader = ParquetReader()
        return reader.read(
            file_bytes=self._get_bytes(use_cache=use_cache),
            **kwargs
        )
    
    def to_dataframe_from_json(
        self,
        orient: str = 'records',
        infer_types: bool = True,
        use_cache: bool = True,
        **kwargs
    ) -> pd.DataFrame:
        """
        Convierte el archivo JSON a DataFrame.
        
        Args:
            orient: Orientación del JSON ('records', 'index', 'columns', etc.)
            infer_types: Si True, infiere tipos automáticamente
            use_cache: Si True, usa bytes en cache si están disponibles
            **kwargs: Argumentos adicionales para pd.read_json
            
        Returns:
            DataFrame con los datos
            
        Examples:
            >>> df = file.get_data().to_json(orient='records')
        """
        reader = JSONReader()
        return reader.read(
            file_bytes=self._get_bytes(use_cache=use_cache),
            orient=orient,
            infer_types=infer_types,
            **kwargs
        )
    
    # Alias para facilitar el uso
    def to_csv(self, **kwargs) -> pd.DataFrame:
        """Alias de to_dataframe_from_csv()."""
        return self.to_dataframe_from_csv(**kwargs)
    
    def to_excel(self, **kwargs) -> pd.DataFrame:
        """Alias de to_dataframe_from_excel()."""
        return self.to_dataframe_from_excel(**kwargs)
    
    def to_parquet(self, **kwargs) -> pd.DataFrame:
        """Alias de to_dataframe_from_parquet()."""
        return self.to_dataframe_from_parquet(**kwargs)
    
    def to_json(self, **kwargs) -> pd.DataFrame:
        """Alias de to_dataframe_from_json()."""
        return self.to_dataframe_from_json(**kwargs)
    
    def clear_cache(self) -> None:
        """Limpia el cache de bytes del archivo."""
        self._bytes_cache = None
