"""
Table Extraction Mixin - PDF table detection and extraction capabilities
"""

import time
import logging
from pathlib import Path
from typing import Dict, Any, List, Optional

# Required
import pdfplumber
import pandas as pd

# Optional — imported lazily in extraction methods

from .base import MCPMixin, mcp_tool
from ..security import validate_pdf_path, parse_pages_parameter, sanitize_error_message

logger = logging.getLogger(__name__)


class TableExtractionMixin(MCPMixin):
    """
    Handles all PDF table extraction operations with intelligent fallbacks.

    Tools provided:
    - extract_tables: Multi-method table extraction with automatic fallbacks
    """

    def get_mixin_name(self) -> str:
        return "TableExtraction"

    def get_required_permissions(self) -> List[str]:
        return ["read_files", "table_processing"]

    def _setup(self):
        """Initialize table extraction specific configuration"""
        self.table_accuracy_threshold = 0.8
        self.max_tables_per_page = 10

    @mcp_tool(
        name="extract_tables",
        description="Extract tables from PDF with automatic method selection and intelligent fallbacks"
    )
    async def extract_tables(
        self,
        pdf_path: str,
        pages: Optional[str] = None,
        method: str = "auto",
        table_format: str = "json"
    ) -> Dict[str, Any]:
        """
        Extract tables from PDF using various methods with automatic fallbacks.

        Args:
            pdf_path: Path to PDF file or URL
            pages: Page specification (e.g., "1-5,10,15-20" or "all")
            method: Extraction method ("auto", "camelot", "tabula", "pdfplumber")
            table_format: Output format ("json", "csv", "markdown")

        Returns:
            Dictionary containing extracted tables and metadata
        """
        start_time = time.time()

        try:
            # Validate inputs using centralized security functions
            path = await validate_pdf_path(pdf_path)
            parsed_pages = parse_pages_parameter(pages)

            all_tables = []
            methods_tried = []

            # Auto method: try methods in order until we find tables
            if method == "auto":
                for try_method in ["camelot", "pdfplumber", "tabula"]:
                    methods_tried.append(try_method)

                    if try_method == "camelot":
                        tables = self._extract_tables_camelot(path, parsed_pages)
                    elif try_method == "pdfplumber":
                        tables = self._extract_tables_pdfplumber(path, parsed_pages)
                    elif try_method == "tabula":
                        tables = self._extract_tables_tabula(path, parsed_pages)

                    if tables:
                        method = try_method
                        all_tables = tables
                        break
            else:
                # Use specific method
                methods_tried.append(method)
                if method == "camelot":
                    all_tables = self._extract_tables_camelot(path, parsed_pages)
                elif method == "pdfplumber":
                    all_tables = self._extract_tables_pdfplumber(path, parsed_pages)
                elif method == "tabula":
                    all_tables = self._extract_tables_tabula(path, parsed_pages)
                else:
                    raise ValueError(f"Unknown table extraction method: {method}")

            # Format tables based on output format
            formatted_tables = []
            for i, df in enumerate(all_tables):
                if table_format == "json":
                    formatted_tables.append({
                        "table_index": i,
                        "data": df.to_dict(orient="records"),
                        "shape": {"rows": len(df), "columns": len(df.columns)}
                    })
                elif table_format == "csv":
                    formatted_tables.append({
                        "table_index": i,
                        "data": df.to_csv(index=False),
                        "shape": {"rows": len(df), "columns": len(df.columns)}
                    })
                elif table_format == "markdown":
                    formatted_tables.append({
                        "table_index": i,
                        "data": df.to_markdown(index=False),
                        "shape": {"rows": len(df), "columns": len(df.columns)}
                    })

            return {
                "success": True,
                "tables": formatted_tables,
                "total_tables": len(formatted_tables),
                "method_used": method,
                "methods_tried": methods_tried,
                "pages_searched": pages or "all",
                "processing_time": round(time.time() - start_time, 2)
            }

        except Exception as e:
            error_msg = sanitize_error_message(str(e))
            logger.error(f"Table extraction failed: {error_msg}")
            return {
                "success": False,
                "error": error_msg,
                "methods_tried": methods_tried,
                "processing_time": round(time.time() - start_time, 2)
            }

    # Private helper methods (all synchronous for proper async pattern)
    def _extract_tables_camelot(self, pdf_path: Path, pages: Optional[List[int]] = None) -> List[pd.DataFrame]:
        """Extract tables using Camelot"""
        import camelot
        page_str = ','.join(map(str, [p+1 for p in pages])) if pages else 'all'

        # Try lattice mode first (for bordered tables)
        try:
            tables = camelot.read_pdf(str(pdf_path), pages=page_str, flavor='lattice')
            if len(tables) > 0:
                return [table.df for table in tables]
        except Exception:
            pass

        # Fall back to stream mode (for borderless tables)
        try:
            tables = camelot.read_pdf(str(pdf_path), pages=page_str, flavor='stream')
            return [table.df for table in tables]
        except Exception:
            return []

    def _extract_tables_tabula(self, pdf_path: Path, pages: Optional[List[int]] = None) -> List[pd.DataFrame]:
        """Extract tables using Tabula"""
        import tabula
        page_list = [p+1 for p in pages] if pages else 'all'

        try:
            tables = tabula.read_pdf(str(pdf_path), pages=page_list, multiple_tables=True)
            return tables
        except Exception:
            return []

    def _extract_tables_pdfplumber(self, pdf_path: Path, pages: Optional[List[int]] = None) -> List[pd.DataFrame]:
        """Extract tables using pdfplumber"""
        tables = []

        with pdfplumber.open(str(pdf_path)) as pdf:
            page_range = pages if pages else range(len(pdf.pages))
            for page_num in page_range:
                page = pdf.pages[page_num]
                page_tables = page.extract_tables()
                for table in page_tables:
                    if table and len(table) > 1:  # Skip empty tables
                        df = pd.DataFrame(table[1:], columns=table[0])
                        tables.append(df)

        return tables