"""
Document Processor pour la segmentation et structuration du texte clinique.

Ce module implémente la segmentation de documents cliniques en sections logiques
et la gestion multi-documents avec priorités de sources.
"""

import re
from typing import List, Dict
from datetime import datetime

from ..models.clinical import (
    ClinicalDocument,
    Section,
    Span,
    StructuredStay,
)
from ..models.metadata import StayMetadata


# Priorités de sources selon les règles PMSI
DOCUMENT_TYPE_PRIORITIES: Dict[str, int] = {
    "cr_operatoire": 1,  # Compte Rendu Opératoire (CRO) - priorité la plus haute
    "cr_medical": 2,  # Compte Rendu Médical (CRM)
    "imagerie": 3,  # Comptes rendus d'imagerie
    "biologie": 4,  # Résultats de biologie
    "courrier": 5,  # Courriers médicaux
    "autre": 5,  # Autres documents
}

# Patterns pour détecter les sections dans les documents cliniques
SECTION_PATTERNS = {
    "anamnese": [
        r"(?i)^[\s]*(?:histoire|anamnèse|antécédents|motif)",
        r"(?i)^[\s]*(?:histoire\s+de\s+la\s+maladie)",
    ],
    "examen": [
        r"(?i)^[\s]*(?:examen|clinique|inspection|palpation|auscultation)",
        r"(?i)^[\s]*(?:examen\s+(?:clinique|physique))",
    ],
    "diagnostic": [
        r"(?i)^[\s]*(?:diagnostic|conclusion\s+diagnostique|impression)",
        r"(?i)^[\s]*(?:diagnostic\s+retenu)",
    ],
    "traitement": [
        r"(?i)^[\s]*(?:traitement|thérapeutique|prescription|conduite)",
        r"(?i)^[\s]*(?:traitement\s+(?:prescrit|instauré))",
    ],
    "evolution": [
        r"(?i)^[\s]*(?:évolution|suites|devenir)",
        r"(?i)^[\s]*(?:évolution\s+(?:clinique|post-opératoire))",
    ],
    "conclusion": [
        r"(?i)^[\s]*(?:conclusion|synthèse|résumé)",
    ],
}


class DocumentProcessor:
    """
    Processeur de documents cliniques.
    
    Responsabilités:
    - Segmentation de documents en sections logiques
    - Gestion multi-documents avec priorités de sources
    - Structuration des séjours
    """

    def __init__(self):
        """Initialise le processeur de documents."""
        # Compiler les patterns de sections pour performance
        self._compiled_patterns = {
            section_type: [re.compile(pattern) for pattern in patterns]
            for section_type, patterns in SECTION_PATTERNS.items()
        }

    def process_documents(
        self,
        documents: List[ClinicalDocument],
        stay_metadata: StayMetadata,
    ) -> StructuredStay:
        """
        Traite une liste de documents pour un séjour.
        
        Args:
            documents: Liste de documents cliniques (non ordonnés)
            stay_metadata: Métadonnées du séjour
            
        Returns:
            StructuredStay avec documents ordonnés par priorité et sections segmentées
            
        Raises:
            ValueError: Si la liste de documents est vide
        """
        if not documents:
            raise ValueError("La liste de documents ne peut pas être vide")

        # Assigner les priorités aux documents selon leur type
        documents_with_priority = []
        for doc in documents:
            priority = DOCUMENT_TYPE_PRIORITIES.get(doc.document_type, 5)
            # Créer une nouvelle instance avec la priorité assignée
            doc_with_priority = ClinicalDocument(
                document_id=doc.document_id,
                document_type=doc.document_type,
                content=doc.content,
                creation_date=doc.creation_date,
                author=doc.author,
                priority=priority,
            )
            documents_with_priority.append(doc_with_priority)

        # Trier les documents par priorité (1 = plus haute priorité)
        sorted_documents = sorted(documents_with_priority, key=lambda d: d.priority)

        # Segmenter tous les documents en sections
        all_sections = []
        for doc in sorted_documents:
            sections = self.segment_document(doc)
            all_sections.extend(sections)

        # Créer le séjour structuré
        structured_stay = StructuredStay(
            stay_id=stay_metadata.stay_id,
            documents=sorted_documents,
            sections=all_sections,
            facts=[],  # Les faits seront extraits par ClinicalFactsExtractor
        )

        return structured_stay

    def segment_document(self, document: ClinicalDocument) -> List[Section]:
        """
        Segmente un document en sections logiques.
        
        Args:
            document: Document clinique à segmenter
            
        Returns:
            Liste de sections détectées dans le document
            
        Notes:
            - Utilise des patterns regex pour détecter les en-têtes de sections
            - Si aucune section n'est détectée, retourne le document entier comme section "autre"
            - Les sections sont ordonnées selon leur position dans le document
        """
        content = document.content
        sections = []

        # Diviser le document en lignes pour détecter les en-têtes
        lines = content.split("\n")
        current_section_type = None
        current_section_start = 0
        current_section_lines = []

        for line_idx, line in enumerate(lines):
            # Vérifier si la ligne correspond à un en-tête de section
            detected_type = self._detect_section_type(line)

            if detected_type:
                # Si on était dans une section, la finaliser
                if current_section_type and current_section_lines:
                    section = self._create_section(
                        document.document_id,
                        current_section_type,
                        current_section_lines,
                        current_section_start,
                        len(sections),
                    )
                    sections.append(section)

                # Commencer une nouvelle section
                current_section_type = detected_type
                current_section_start = sum(len(l) + 1 for l in lines[:line_idx])
                current_section_lines = [line]
            else:
                # Continuer la section courante
                if current_section_type:
                    current_section_lines.append(line)
                else:
                    # Pas encore de section détectée, accumuler dans "autre"
                    if not current_section_lines:
                        current_section_type = "autre"
                        current_section_start = 0
                    current_section_lines.append(line)

        # Finaliser la dernière section
        if current_section_type and current_section_lines:
            section = self._create_section(
                document.document_id,
                current_section_type,
                current_section_lines,
                current_section_start,
                len(sections),
            )
            sections.append(section)

        # Si aucune section n'a été détectée, créer une section "autre" avec tout le contenu
        if not sections:
            section = Section(
                section_id=f"{document.document_id}_section_0",
                section_type="autre",
                content=content,
                span=Span(start=0, end=len(content)),
            )
            sections.append(section)

        return sections

    def _detect_section_type(self, line: str) -> str | None:
        """
        Détecte le type de section à partir d'une ligne.
        
        Args:
            line: Ligne de texte à analyser
            
        Returns:
            Type de section détecté ou None si aucune correspondance
        """
        line_stripped = line.strip()
        if not line_stripped:
            return None

        # Vérifier chaque type de section
        for section_type, patterns in self._compiled_patterns.items():
            for pattern in patterns:
                if pattern.match(line_stripped):
                    return section_type

        return None

    def _create_section(
        self,
        document_id: str,
        section_type: str,
        lines: List[str],
        start_pos: int,
        section_idx: int,
    ) -> Section:
        """
        Crée une section à partir de lignes de texte.
        
        Args:
            document_id: ID du document source
            section_type: Type de section
            lines: Lignes de texte de la section
            start_pos: Position de début dans le document
            section_idx: Index de la section
            
        Returns:
            Section créée
        """
        content = "\n".join(lines)
        end_pos = start_pos + len(content)

        section = Section(
            section_id=f"{document_id}_section_{section_idx}",
            section_type=section_type,
            content=content,
            span=Span(start=start_pos, end=end_pos),
        )

        return section
