from llama_parse import LlamaParse
from typing import Optional, List, Dict, Any
import os
from src.core.config import settings
from src.core.logging import logger


class PDFParser:
    """
    A class to parse PDF documents using LlamaParse API.
    Supports both file paths and URLs for extracting structured text from PDFs.
    """
    
    def __init__(self, api_key: Optional[str] = None, result_type: str = "markdown"):
        """
        Initialize the PDF parser.
        
        Args:
            api_key: LlamaParse API key. If None, uses settings.LLAMA_CLOUD_API_KEY
            result_type: Output format - "markdown" or "text"
        """
        self.api_key = api_key or settings.LLAMA_CLOUD_API_KEY
        if not self.api_key:
            raise ValueError("LLAMA_CLOUD_API_KEY must be configured in environment variables")
        
        self.parser = LlamaParse(
            api_key=self.api_key,
            result_type=result_type,
            verbose=True
        )
        logger.info(f"PDFParser initialized with result_type={result_type}")
    
    def parse_url(self, url: str) -> List[Dict[str, Any]]:
        """
        Parse a PDF from a URL.
        
        Args:
            url: URL of the PDF document
            
        Returns:
            List of parsed documents with text content
            
        Raises:
            Exception: If PDF parsing fails
        """
        try:
            logger.info(f"Parsing PDF from URL: {url}")
            documents = self.parser.load_data(url)
            logger.info(f"Successfully parsed {len(documents)} documents from URL")
            return documents
        except ConnectionError as e:
            logger.error(f"LLAMAPARSE FAILURE REASON: Network connection error - {str(e)}")
            raise Exception(f"LlamaParse network error: {str(e)}")
        except TimeoutError as e:
            logger.error(f"LLAMAPARSE FAILURE REASON: Request timeout - {str(e)}")
            raise Exception(f"LlamaParse timeout: {str(e)}")
        except Exception as e:
            error_msg = str(e).lower()
            if 'tcptransport closed' in error_msg or 'handler is closed' in error_msg:
                logger.error(f"LLAMAPARSE FAILURE REASON: HTTP connection pool exhausted/closed - {str(e)}")
                raise Exception(f"LlamaParse connection pool issue: {str(e)}")
            elif 'rate limit' in error_msg or '429' in error_msg:
                logger.error(f"LLAMAPARSE FAILURE REASON: API rate limit exceeded - {str(e)}")
                raise Exception(f"LlamaParse rate limit: {str(e)}")
            elif 'authentication' in error_msg or '401' in error_msg or '403' in error_msg:
                logger.error(f"LLAMAPARSE FAILURE REASON: API key authentication failed - {str(e)}")
                raise Exception(f"LlamaParse auth error: {str(e)}")
            elif 'service unavailable' in error_msg or '503' in error_msg:
                logger.error(f"LLAMAPARSE FAILURE REASON: LlamaParse service unavailable - {str(e)}")
                raise Exception(f"LlamaParse service down: {str(e)}")
            else:
                logger.error(f"LLAMAPARSE FAILURE REASON: Unknown error - {str(e)}")
                raise Exception(f"LlamaParse error: {str(e)}")
    
    def parse_file(self, file_path: str) -> List[Dict[str, Any]]:
        """
        Parse a local PDF file.
        
        Args:
            file_path: Path to the PDF file
            
        Returns:
            List of parsed documents with text content
            
        Raises:
            Exception: If PDF parsing fails
        """
        try:
            logger.info(f"Parsing PDF from file: {file_path}")
            documents = self.parser.load_data(file_path)
            logger.info(f"Successfully parsed {len(documents)} documents from file")
            return documents
        except Exception as e:
            logger.error(f"Error parsing PDF file {file_path}: {str(e)}")
            raise Exception(f"Error parsing PDF file: {str(e)}")
    
    def parse_multiple(self, sources: List[str]) -> List[Dict[str, Any]]:
        """
        Parse multiple PDFs from URLs or file paths.
        
        Args:
            sources: List of URLs or file paths
            
        Returns:
            List of parsed documents from all sources
        """
        all_documents = []
        logger.info(f"Parsing {len(sources)} PDF sources")
        
        for source in sources:
            try:
                docs = self.parser.load_data(source)
                all_documents.extend(docs)
                logger.info(f"Successfully parsed {source}")
            except Exception as e:
                logger.error(f"Error parsing {source}: {str(e)}")
        
        logger.info(f"Total documents parsed: {len(all_documents)}")
        return all_documents
    
    def get_text(self, documents: List[Dict[str, Any]]) -> str:
        """
        Extract plain text from parsed documents.
        
        Args:
            documents: List of parsed documents
            
        Returns:
            Combined text from all documents
        """
        if not documents:
            return ""
        return "\n\n".join([doc.text for doc in documents])


def get_pdf_parser() -> PDFParser:
    """
    Factory function to create a new parser instance per request.
    This avoids connection pool conflicts in async/concurrent scenarios.
    """
    return PDFParser()


# Singleton instance for backward compatibility (deprecated - use get_pdf_parser() instead)
pdf_parser = PDFParser()
