"""
Core module for qry-doc library.

This module provides the QryDoc class, the main entry point (Facade)
for all library functionality.
"""
import os
from pathlib import Path
from typing import Any, Optional, Union

import pandas as pd

from qry_doc.ai_adapter import PandasAIAdapter
from qry_doc.chart_manager import ChartManager
from qry_doc.csv_exporter import CSVExporter
from qry_doc.data_source import DataSourceLoader
from qry_doc.exceptions import (
    QryDocError,
    QueryError,
    ExportError,
    ReportError,
    DataSourceError,
)
from qry_doc.report_generator import ReportGenerator
from qry_doc.report_template import ReportTemplate, DEFAULT_TEMPLATE
from qry_doc.validators import OutputValidator


class QryDoc:
    """
    Main entry point for qry-doc library.
    
    QryDoc acts as a Facade, providing a simple interface to:
    - Ask natural language questions about data
    - Extract query results to CSV
    - Generate PDF reports with charts and tables
    
    Example:
        ```python
        from qry_doc import QryDoc
        from pandasai.llm import OpenAI
        
        llm = OpenAI(api_token="your-api-key")
        qry = QryDoc("data.csv", llm=llm)
        
        # Ask a question
        answer = qry.ask("What is the average sales?")
        
        # Export to CSV
        qry.extract_to_csv("Show top 10 customers", "top_customers.csv")
        
        # Generate PDF report
        qry.generate_report("Analyze sales trends", "report.pdf")
        ```
    """
    
    # Environment variable for API key fallback
    API_KEY_ENV_VAR = "OPENAI_API_KEY"
    
    def __init__(
        self,
        data_source: Union[str, Path, pd.DataFrame],
        llm: Any,
        api_key: Optional[str] = None
    ) -> None:
        """
        Initialize QryDoc with a data source and LLM provider.
        
        Args:
            data_source: Path to CSV file, pandas DataFrame, or SQL connection string.
            llm: LLM provider instance (OpenAI, Anthropic, LiteLLM, etc.).
            api_key: Optional API key (falls back to environment variable).
            
        Raises:
            DataSourceError: If the data source cannot be loaded.
        """
        # Load data
        self._df = DataSourceLoader.load(data_source)
        
        # Store LLM
        self._llm = llm
        
        # Handle API key (for reference, actual key is in llm)
        self._api_key = api_key or os.environ.get(self.API_KEY_ENV_VAR)
        
        # Initialize chart manager
        self._chart_manager = ChartManager()
        
        # Initialize AI adapter
        self._adapter = PandasAIAdapter(
            df=self._df,
            llm=self._llm,
            chart_save_path=self._chart_manager.path
        )
    
    def ask(self, query: str) -> str:
        """
        Ask a natural language question about the data.
        
        Args:
            query: Natural language question.
            
        Returns:
            A human-readable text response.
            
        Raises:
            QueryError: If the query cannot be processed.
            
        Example:
            ```python
            answer = qry.ask("What is the total revenue for 2023?")
            print(answer)  # "The total revenue for 2023 is $1,234,567"
            ```
        """
        try:
            return self._adapter.query_as_text(query)
        except QueryError:
            raise
        except Exception as e:
            sanitized = OutputValidator.sanitize_error_message(e)
            raise QueryError(
                user_message=f"No se pudo interpretar la consulta: {sanitized}. "
                            "Intente reformular la pregunta con más detalle.",
                internal_error=e
            )
    
    def extract_to_csv(
        self,
        query: str,
        output_path: Union[str, Path],
        include_index: bool = False
    ) -> str:
        """
        Execute a query and export results to CSV.
        
        Args:
            query: Natural language query requesting tabular data.
            output_path: Path for the output CSV file.
            include_index: Whether to include DataFrame index. Default False.
            
        Returns:
            Confirmation message with the output file path.
            
        Raises:
            QueryError: If the query cannot be processed.
            ExportError: If the result is not tabular or export fails.
            
        Example:
            ```python
            result = qry.extract_to_csv(
                "Show sales by region for Q4",
                "q4_sales.csv"
            )
            print(result)  # "Datos exportados exitosamente a q4_sales.csv"
            ```
        """
        try:
            # Get DataFrame result
            df = self._adapter.query_as_dataframe(query)
            
            # Validate result
            is_valid, error_msg = OutputValidator.validate_dataframe(df)
            if not is_valid:
                raise ExportError(user_message=error_msg)
            
            # Export to CSV
            return CSVExporter.export(
                df=df,
                path=output_path,
                include_index=include_index
            )
            
        except (QueryError, ExportError):
            raise
        except Exception as e:
            sanitized = OutputValidator.sanitize_error_message(e)
            raise ExportError(
                user_message=f"Error al exportar datos: {sanitized}",
                internal_error=e
            )
    
    def generate_report(
        self,
        query: str,
        output_path: Union[str, Path],
        title: str = "Reporte Automático",
        template: Optional[ReportTemplate] = None
    ) -> str:
        """
        Generate a PDF report based on a query.
        
        The report includes:
        - Title and executive summary
        - Relevant charts/visualizations (if applicable)
        - Data tables (if applicable)
        
        Args:
            query: Natural language query describing the analysis.
            output_path: Path for the output PDF file.
            title: Report title. Default "Reporte Automático".
            template: Optional ReportTemplate for custom styling.
            
        Returns:
            Confirmation message with the output file path.
            
        Raises:
            QueryError: If the query cannot be processed.
            ReportError: If report generation fails.
            
        Example:
            ```python
            from qry_doc import ReportTemplate
            
            template = ReportTemplate(
                logo_path="logo.png",
                primary_color="#003366"
            )
            
            result = qry.generate_report(
                "Analyze customer demographics",
                "demographics_report.pdf",
                title="Customer Demographics Analysis",
                template=template
            )
            ```
        """
        try:
            # Clear previous charts
            self._chart_manager.clear_charts()
            
            # Step 1: Get textual summary
            summary = self._adapter.query_as_text(
                f"Analyze the data regarding: {query}. Provide a comprehensive summary."
            )
            
            # Step 2: Try to generate a chart
            chart_path = None
            try:
                self._adapter.query(f"Create a chart or visualization showing: {query}")
                chart_path = self._chart_manager.get_latest_chart()
            except Exception:
                # Chart generation is optional
                pass
            
            # Step 3: Try to get tabular data
            table_data = None
            try:
                table_data = self._adapter.query_as_dataframe(
                    f"Create a summary table for: {query}"
                )
                # Validate table
                is_valid, _ = OutputValidator.validate_dataframe(table_data)
                if not is_valid:
                    table_data = None
            except Exception:
                # Table is optional
                pass
            
            # Step 4: Generate PDF
            generator = ReportGenerator(
                output_path=output_path,
                template=template or DEFAULT_TEMPLATE
            )
            
            generator.build(
                title=title,
                summary=summary,
                chart_path=chart_path,
                dataframe=table_data
            )
            
            return f"Reporte generado exitosamente en {output_path}"
            
        except (QueryError, ReportError):
            raise
        except Exception as e:
            sanitized = OutputValidator.sanitize_error_message(e)
            raise ReportError(
                user_message=f"Error al generar el reporte: {sanitized}",
                internal_error=e
            )
        finally:
            # Clean up charts
            self._chart_manager.clear_charts()
    
    @property
    def dataframe(self) -> pd.DataFrame:
        """Get the underlying DataFrame."""
        return self._df
    
    @property
    def columns(self) -> list[str]:
        """Get the column names of the data."""
        return list(self._df.columns)
    
    @property
    def shape(self) -> tuple[int, int]:
        """Get the shape of the data (rows, columns)."""
        return self._df.shape
    
    def __enter__(self) -> "QryDoc":
        """Enter context manager."""
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        """Exit context manager, cleaning up resources."""
        self._chart_manager.cleanup()
    
    def __del__(self) -> None:
        """Destructor - ensure cleanup."""
        if hasattr(self, '_chart_manager'):
            self._chart_manager.cleanup()
