"""
Data source loading utilities for qry-doc.

This module provides functionality to load data from various sources
including CSV files, pandas DataFrames, and SQL connections.
"""
import re
from pathlib import Path
from typing import Union

import pandas as pd

from qry_doc.exceptions import DataSourceError


class DataSourceLoader:
    """
    Loads data from various sources into pandas DataFrames.
    
    Supports:
    - CSV file paths
    - pandas DataFrames (passthrough)
    - SQL connection strings (future)
    """
    
    # Supported CSV extensions
    CSV_EXTENSIONS = {".csv", ".tsv", ".txt"}
    
    # SQL connection string patterns
    SQL_PATTERNS = [
        re.compile(r"^(postgresql|postgres)://", re.IGNORECASE),
        re.compile(r"^mysql://", re.IGNORECASE),
        re.compile(r"^sqlite://", re.IGNORECASE),
        re.compile(r"^mssql://", re.IGNORECASE),
        re.compile(r"^oracle://", re.IGNORECASE),
    ]
    
    @classmethod
    def load(cls, source: Union[str, Path, pd.DataFrame]) -> pd.DataFrame:
        """
        Load data from the given source into a DataFrame.
        
        Args:
            source: Can be:
                - A path to a CSV file (str or Path)
                - A pandas DataFrame (returned as-is)
                - A SQL connection string (future support)
        
        Returns:
            A pandas DataFrame containing the loaded data.
            
        Raises:
            DataSourceError: If the source format is not supported or loading fails.
        """
        # Handle DataFrame passthrough
        if isinstance(source, pd.DataFrame):
            return source
        
        # Handle Path objects
        if isinstance(source, Path):
            source = str(source)
        
        # Must be a string at this point
        if not isinstance(source, str):
            raise DataSourceError(
                user_message=(
                    f"Formato de datos no soportado (tipo: {type(source).__name__}). "
                    "Use: ruta a archivo CSV, DataFrame de pandas, o cadena de conexión SQL."
                )
            )
        
        # Check if it's a CSV path
        if cls.is_csv_path(source):
            return cls._load_csv(source)
        
        # Check if it's a SQL connection
        if cls.is_sql_connection(source):
            return cls._load_sql(source)
        
        # Unknown format
        raise DataSourceError(
            user_message=(
                f"Formato de datos no reconocido: '{source[:50]}{'...' if len(source) > 50 else ''}'. "
                "Use: ruta a archivo CSV (.csv, .tsv), DataFrame de pandas, "
                "o cadena de conexión SQL (postgresql://, mysql://, sqlite://)."
            )
        )
    
    @classmethod
    def is_csv_path(cls, source: str) -> bool:
        """
        Check if the source string looks like a CSV file path.
        
        Args:
            source: The string to check.
            
        Returns:
            True if it appears to be a CSV path.
        """
        if not source:
            return False
        
        path = Path(source)
        
        # Check extension
        if path.suffix.lower() in cls.CSV_EXTENSIONS:
            return True
        
        # Check if file exists and has CSV-like extension
        if path.exists() and path.is_file():
            return path.suffix.lower() in cls.CSV_EXTENSIONS
        
        return False
    
    @classmethod
    def is_sql_connection(cls, source: str) -> bool:
        """
        Check if the source string looks like a SQL connection string.
        
        Args:
            source: The string to check.
            
        Returns:
            True if it appears to be a SQL connection string.
        """
        if not source:
            return False
        
        for pattern in cls.SQL_PATTERNS:
            if pattern.match(source):
                return True
        
        return False
    
    @classmethod
    def _load_csv(cls, path: str) -> pd.DataFrame:
        """
        Load a CSV file into a DataFrame.
        
        Args:
            path: Path to the CSV file.
            
        Returns:
            DataFrame with the CSV contents.
            
        Raises:
            DataSourceError: If the file cannot be read.
        """
        file_path = Path(path)
        
        if not file_path.exists():
            raise DataSourceError(
                user_message=f"El archivo no existe: {file_path.name}"
            )
        
        if not file_path.is_file():
            raise DataSourceError(
                user_message=f"La ruta no es un archivo: {file_path.name}"
            )
        
        try:
            # Detect separator based on extension
            sep = "\t" if file_path.suffix.lower() == ".tsv" else ","
            
            # Try to read with UTF-8 first, then fallback to latin-1
            try:
                df = pd.read_csv(file_path, sep=sep, encoding="utf-8")
            except UnicodeDecodeError:
                df = pd.read_csv(file_path, sep=sep, encoding="latin-1")
            
            return df
            
        except pd.errors.EmptyDataError:
            raise DataSourceError(
                user_message=f"El archivo está vacío: {file_path.name}"
            )
        except pd.errors.ParserError as e:
            raise DataSourceError(
                user_message=f"Error al parsear el archivo CSV: {file_path.name}",
                internal_error=e
            )
        except PermissionError:
            raise DataSourceError(
                user_message=f"Sin permisos para leer el archivo: {file_path.name}"
            )
        except Exception as e:
            raise DataSourceError(
                user_message=f"Error al cargar el archivo: {file_path.name}",
                internal_error=e
            )
    
    @classmethod
    def _load_sql(cls, connection_string: str) -> pd.DataFrame:
        """
        Load data from a SQL database.
        
        Note: This is a placeholder for future SQL support.
        Currently raises NotImplementedError.
        
        Args:
            connection_string: SQL connection string.
            
        Raises:
            DataSourceError: SQL support not yet implemented.
        """
        raise DataSourceError(
            user_message=(
                "Soporte para conexiones SQL aún no implementado. "
                "Por favor, exporte sus datos a CSV o use un DataFrame de pandas."
            )
        )
