"""
File Processor Service - Dokumentenverarbeitung fuer BreakPilot.

Shared Service fuer:
- OCR (Optical Character Recognition) fuer Handschrift und gedruckten Text
- PDF-Parsing und Textextraktion
- Bildverarbeitung und -optimierung
- DOCX/DOC Textextraktion

Verwendet:
- PaddleOCR fuer deutsche Handschrift (via ImageProcessor)
- PyMuPDF fuer PDF-Verarbeitung
- python-docx fuer DOCX-Dateien
- OpenCV fuer Bildvorverarbeitung (via ImageProcessor)
"""

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

from PIL import Image

from .file_processor_types import (
    FileType,
    ProcessingMode,
    ProcessedRegion,
    ProcessingResult,
)
from .image_processing import ImageProcessor

logger = logging.getLogger(__name__)

__all__ = [
    "FileType",
    "ProcessingMode",
    "ProcessedRegion",
    "ProcessingResult",
    "FileProcessor",
    "get_file_processor",
    "process_file",
    "extract_text_from_pdf",
    "ocr_image",
    "ocr_handwriting",
]


class FileProcessor:
    """
    Zentrale Dokumentenverarbeitung fuer BreakPilot.

    Unterstuetzt:
    - Handschrifterkennung (OCR) fuer Klausuren
    - Textextraktion aus PDFs
    - DOCX/DOC Verarbeitung
    - Bildvorverarbeitung fuer bessere OCR-Ergebnisse
    """

    def __init__(self, ocr_lang: str = "de", use_gpu: bool = False):
        """
        Initialisiert den File Processor.

        Args:
            ocr_lang: Sprache fuer OCR (default: "de" fuer Deutsch)
            use_gpu: GPU fuer OCR nutzen (beschleunigt Verarbeitung)
        """
        self.ocr_lang = ocr_lang
        self.use_gpu = use_gpu
        self._image_processor = ImageProcessor(ocr_lang=ocr_lang, use_gpu=use_gpu)

        logger.info(f"FileProcessor initialized (lang={ocr_lang}, gpu={use_gpu})")

    def detect_file_type(
        self, file_path: str = None, file_bytes: bytes = None
    ) -> FileType:
        """
        Erkennt den Dateityp.

        Args:
            file_path: Pfad zur Datei
            file_bytes: Dateiinhalt als Bytes

        Returns:
            FileType enum
        """
        if file_path:
            ext = Path(file_path).suffix.lower()
            if ext == ".pdf":
                return FileType.PDF
            elif ext in [".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".gif"]:
                return FileType.IMAGE
            elif ext == ".docx":
                return FileType.DOCX
            elif ext == ".doc":
                return FileType.DOC
            elif ext == ".txt":
                return FileType.TXT

        if file_bytes:
            # Magic number detection
            if file_bytes[:4] == b'%PDF':
                return FileType.PDF
            elif file_bytes[:8] == b'\x89PNG\r\n\x1a\n':
                return FileType.IMAGE
            elif file_bytes[:2] in [b'\xff\xd8', b'BM']:  # JPEG, BMP
                return FileType.IMAGE
            elif file_bytes[:4] == b'PK\x03\x04':  # ZIP (DOCX)
                return FileType.DOCX

        return FileType.UNKNOWN

    def process(
        self,
        file_path: str = None,
        file_bytes: bytes = None,
        mode: ProcessingMode = ProcessingMode.MIXED
    ) -> ProcessingResult:
        """
        Verarbeitet ein Dokument.

        Args:
            file_path: Pfad zur Datei
            file_bytes: Dateiinhalt als Bytes
            mode: Verarbeitungsmodus

        Returns:
            ProcessingResult mit extrahiertem Text und Metadaten
        """
        if not file_path and not file_bytes:
            raise ValueError(
                "Entweder file_path oder file_bytes muss angegeben werden"
            )

        file_type = self.detect_file_type(file_path, file_bytes)
        logger.info(f"Processing file of type: {file_type}")

        if file_type == FileType.PDF:
            return self._process_pdf(file_path, file_bytes, mode)
        elif file_type == FileType.IMAGE:
            return self._process_image(file_path, file_bytes, mode)
        elif file_type == FileType.DOCX:
            return self._process_docx(file_path, file_bytes)
        elif file_type == FileType.TXT:
            return self._process_txt(file_path, file_bytes)
        else:
            raise ValueError(f"Nicht unterstuetzter Dateityp: {file_type}")

    def _process_pdf(
        self,
        file_path: str = None,
        file_bytes: bytes = None,
        mode: ProcessingMode = ProcessingMode.MIXED
    ) -> ProcessingResult:
        """Verarbeitet PDF-Dateien."""
        try:
            import fitz  # PyMuPDF
        except ImportError:
            logger.warning("PyMuPDF nicht installiert - versuche Fallback")
            return self._process_image(file_path, file_bytes, mode)

        if file_bytes:
            doc = fitz.open(stream=file_bytes, filetype="pdf")
        else:
            doc = fitz.open(file_path)

        all_text = []
        all_regions = []
        total_confidence = 0.0
        region_count = 0

        for page_num, page in enumerate(doc, start=1):
            page_text = page.get_text()

            if page_text.strip() and mode != ProcessingMode.OCR_HANDWRITING:
                all_text.append(page_text)
                all_regions.append(ProcessedRegion(
                    text=page_text,
                    confidence=1.0,
                    bbox=(0, 0, int(page.rect.width), int(page.rect.height)),
                    page=page_num
                ))
                total_confidence += 1.0
                region_count += 1
            else:
                # Seite als Bild rendern und OCR anwenden
                pix = page.get_pixmap(matrix=fitz.Matrix(2, 2))
                img_bytes = pix.tobytes("png")
                img = Image.open(io.BytesIO(img_bytes))

                ocr_result = self._image_processor.ocr_image(img)
                all_text.append(ocr_result["text"])

                for region in ocr_result["regions"]:
                    region.page = page_num
                    all_regions.append(region)
                    total_confidence += region.confidence
                    region_count += 1

        doc.close()

        avg_confidence = (
            total_confidence / region_count if region_count > 0 else 0.0
        )

        return ProcessingResult(
            text="\n\n".join(all_text),
            confidence=avg_confidence,
            regions=all_regions,
            page_count=len(doc) if hasattr(doc, '__len__') else 1,
            file_type=FileType.PDF,
            processing_mode=mode,
            metadata={"source": file_path or "bytes"}
        )

    def _process_image(
        self,
        file_path: str = None,
        file_bytes: bytes = None,
        mode: ProcessingMode = ProcessingMode.MIXED
    ) -> ProcessingResult:
        """Verarbeitet Bilddateien."""
        if file_bytes:
            img = Image.open(io.BytesIO(file_bytes))
        else:
            img = Image.open(file_path)

        processed_img = self._image_processor.preprocess_image(img)
        ocr_result = self._image_processor.ocr_image(processed_img)

        return ProcessingResult(
            text=ocr_result["text"],
            confidence=ocr_result["confidence"],
            regions=ocr_result["regions"],
            page_count=1,
            file_type=FileType.IMAGE,
            processing_mode=mode,
            metadata={
                "source": file_path or "bytes",
                "image_size": img.size
            }
        )

    def _process_docx(
        self,
        file_path: str = None,
        file_bytes: bytes = None
    ) -> ProcessingResult:
        """Verarbeitet DOCX-Dateien."""
        try:
            from docx import Document
        except ImportError:
            raise ImportError("python-docx ist nicht installiert")

        if file_bytes:
            doc = Document(io.BytesIO(file_bytes))
        else:
            doc = Document(file_path)

        paragraphs = []
        for para in doc.paragraphs:
            if para.text.strip():
                paragraphs.append(para.text)

        for table in doc.tables:
            for row in table.rows:
                row_text = " | ".join(cell.text for cell in row.cells)
                if row_text.strip():
                    paragraphs.append(row_text)

        text = "\n\n".join(paragraphs)

        return ProcessingResult(
            text=text,
            confidence=1.0,
            regions=[ProcessedRegion(
                text=text, confidence=1.0, bbox=(0, 0, 0, 0), page=1
            )],
            page_count=1,
            file_type=FileType.DOCX,
            processing_mode=ProcessingMode.TEXT_EXTRACT,
            metadata={"source": file_path or "bytes"}
        )

    def _process_txt(
        self,
        file_path: str = None,
        file_bytes: bytes = None
    ) -> ProcessingResult:
        """Verarbeitet Textdateien."""
        if file_bytes:
            text = file_bytes.decode('utf-8', errors='ignore')
        else:
            with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                text = f.read()

        return ProcessingResult(
            text=text,
            confidence=1.0,
            regions=[ProcessedRegion(
                text=text, confidence=1.0, bbox=(0, 0, 0, 0), page=1
            )],
            page_count=1,
            file_type=FileType.TXT,
            processing_mode=ProcessingMode.TEXT_EXTRACT,
            metadata={"source": file_path or "bytes"}
        )

    def extract_handwriting_regions(
        self,
        img: Image.Image,
        min_area: int = 500
    ) -> List[Dict[str, Any]]:
        """Delegate to ImageProcessor."""
        return self._image_processor.extract_handwriting_regions(img, min_area)


# Singleton-Instanz
_file_processor: Optional[FileProcessor] = None


def get_file_processor() -> FileProcessor:
    """Gibt Singleton-Instanz des File Processors zurueck."""
    global _file_processor
    if _file_processor is None:
        _file_processor = FileProcessor()
    return _file_processor


# Convenience functions
def process_file(
    file_path: str = None,
    file_bytes: bytes = None,
    mode: ProcessingMode = ProcessingMode.MIXED
) -> ProcessingResult:
    """Convenience function zum Verarbeiten einer Datei."""
    processor = get_file_processor()
    return processor.process(file_path, file_bytes, mode)


def extract_text_from_pdf(
    file_path: str = None, file_bytes: bytes = None
) -> str:
    """Extrahiert Text aus einer PDF-Datei."""
    result = process_file(file_path, file_bytes, ProcessingMode.TEXT_EXTRACT)
    return result.text


def ocr_image(file_path: str = None, file_bytes: bytes = None) -> str:
    """Fuehrt OCR auf einem Bild aus."""
    result = process_file(file_path, file_bytes, ProcessingMode.OCR_PRINTED)
    return result.text


def ocr_handwriting(file_path: str = None, file_bytes: bytes = None) -> str:
    """Fuehrt Handschrift-OCR auf einem Bild aus."""
    result = process_file(file_path, file_bytes, ProcessingMode.OCR_HANDWRITING)
    return result.text
