#!/usr/bin/env python3
"""
Filtre pour éliminer les faux positifs liés aux informations hospitalières.

Les informations d'établissements de santé (adresses, téléphones, etc.) sont publiques
et ne doivent pas être anonymisées.
"""

import re
import yaml
from pathlib import Path
from typing import List, Dict, Set

class HospitalFilter:
    """Filtre les détections qui correspondent à des informations hospitalières."""
    
    def __init__(self, config_path: str = "config/hospital_stopwords.yml"):
        """
        Initialise le filtre avec la configuration.
        
        Args:
            config_path: Chemin vers le fichier de configuration YAML
        """
        self.config_path = Path(config_path)
        self.hospital_addresses: Set[str] = set()
        self.hospital_postal_codes: Set[str] = set()
        self.hospital_cities: Set[str] = set()
        self.hospital_phones: Set[str] = set()
        self.hospital_phone_patterns: List[re.Pattern] = []
        self.anatomical_terms: Set[str] = set()
        self.episode_filename_patterns: List[re.Pattern] = []
        
        self._load_config()
    
    def _load_config(self):
        """Charge la configuration depuis le fichier YAML."""
        if not self.config_path.exists():
            print(f"⚠️  Configuration non trouvée: {self.config_path}")
            return
        
        with open(self.config_path, 'r', encoding='utf-8') as f:
            config = yaml.safe_load(f)
        
        # Charger les listes
        self.hospital_addresses = set(
            addr.upper() for addr in config.get('hospital_addresses', [])
        )
        self.hospital_postal_codes = set(
            cp.upper() for cp in config.get('hospital_postal_codes', [])
        )
        self.hospital_cities = set(
            city.upper() for city in config.get('hospital_cities', [])
        )
        self.hospital_phones = set(config.get('hospital_phones', []))
        self.anatomical_terms = set(
            term.upper() for term in config.get('anatomical_terms', [])
        )
        
        # Compiler les patterns regex
        for pattern in config.get('hospital_phone_patterns', []):
            self.hospital_phone_patterns.append(re.compile(pattern))
        
        for pattern in config.get('episode_filename_patterns', []):
            self.episode_filename_patterns.append(re.compile(pattern))
    
    def is_hospital_address(self, text: str) -> bool:
        """Vérifie si le texte est une adresse d'hôpital."""
        text_upper = text.upper().strip()
        
        # Correspondance exacte
        if text_upper in self.hospital_addresses:
            return True
        
        # Correspondance partielle (pour gérer les variations)
        for hospital_addr in self.hospital_addresses:
            if hospital_addr in text_upper or text_upper in hospital_addr:
                return True
        
        return False
    
    def is_hospital_postal_code(self, text: str) -> bool:
        """Vérifie si le texte est un code postal d'hôpital."""
        text_upper = text.upper().strip()
        
        # Correspondance exacte
        if text_upper in self.hospital_postal_codes:
            return True
        
        # Vérifier si contient "CEDEX" (indicateur d'établissement)
        if "CEDEX" in text_upper:
            return True
        
        return False
    
    def is_hospital_city(self, text: str) -> bool:
        """Vérifie si le texte est une ville d'hôpital."""
        text_upper = text.upper().strip()
        
        # Correspondance exacte
        if text_upper in self.hospital_cities:
            return True
        
        # Vérifier si contient "CEDEX"
        if "CEDEX" in text_upper:
            return True
        
        # Vérifier si c'est un terme anatomique
        if text_upper in self.anatomical_terms:
            return True
        
        return False
    
    def is_hospital_phone(self, text: str) -> bool:
        """Vérifie si le texte est un téléphone d'hôpital."""
        text_clean = text.strip()
        
        # Correspondance exacte
        if text_clean in self.hospital_phones:
            return True
        
        # Vérifier les patterns
        for pattern in self.hospital_phone_patterns:
            if pattern.match(text_clean):
                return True
        
        return False
    
    def is_episode_in_filename(self, text: str, filename: str = "") -> bool:
        """
        Vérifie si le numéro d'épisode provient du nom de fichier.
        
        Ces numéros apparaissent dans les métadonnées/en-têtes mais pas dans le contenu patient.
        Cas spécial : documents trackare où le numéro d'épisode est répété sur chaque page.
        """
        if not filename:
            return False
        
        # Extraire juste le nom de fichier sans extension
        filename_base = Path(filename).stem if isinstance(filename, str) else filename
        
        # Pattern trackare : trackare-XXXXXXXX-YYYYYYYY où YYYYYYYY est le numéro d'épisode
        trackare_match = re.search(r'trackare-\d+-(\d+)', filename_base, re.IGNORECASE)
        if trackare_match:
            episode_from_filename = trackare_match.group(1)
            # Vérifier si le texte détecté correspond au numéro d'épisode du fichier
            if text.strip() == episode_from_filename:
                return True
            # Vérifier aussi avec le pattern "N° Episode XXXXXXXX"
            if f"N° Episode {episode_from_filename}" in text or f"N° Épisode {episode_from_filename}" in text:
                return True
        
        # Ne PAS filtrer les épisodes dans les autres types de documents (CRH, CRO, etc.)
        # Ces documents contiennent des épisodes légitimes dans le contenu patient
        return False
    
    def should_filter(self, pii_type: str, text: str, filename: str = "", page: int = -1) -> bool:
        """
        Détermine si une détection doit être filtrée (= faux positif).
        
        Args:
            pii_type: Type de PII (ADRESSE, TEL, VILLE, etc.)
            text: Texte détecté
            filename: Nom du fichier source (optionnel)
            page: Numéro de page (-1 = métadonnées)
        
        Returns:
            True si la détection doit être filtrée (faux positif)
        """
        # ADRESSE, CODE_POSTAL, VILLE, TEL : NE PAS filtrer.
        # Les coordonnées hospitalières identifient indirectement le patient
        # et doivent être masquées (validé par contrôle humain 2026-03-12).

        # EPISODE : NE PAS filtrer.
        # Les numéros d'épisode identifient le patient (validé 2026-03-14).

        return False
    
    def filter_detections(self, detections: List[Dict], filename: str = "", is_trackare: bool = False) -> List[Dict]:
        """
        Filtre une liste de détections pour éliminer les faux positifs.
        
        Args:
            detections: Liste de détections (format: {'kind': ..., 'original': ..., 'page': ...})
            filename: Nom du fichier source
            is_trackare: True si le document est un export Trackare/TrakCare
        
        Returns:
            Liste de détections filtrées
        """
        filtered = []
        
        for det in detections:
            pii_type = det.get('kind', '')
            text = det.get('original', '')
            page = det.get('page', -1)
            
            # Pour les documents trackare, filtrer les EPISODE qui correspondent au nom de fichier
            if is_trackare and pii_type == "EPISODE":
                if self.is_episode_in_filename(text, filename):
                    continue  # Filtrer ce faux positif
            
            if not self.should_filter(pii_type, text, filename, page):
                filtered.append(det)
        
        return filtered


# Test du filtre
if __name__ == "__main__":
    filter = HospitalFilter()
    
    # Tests
    test_cases = [
        # ADRESSE, CODE_POSTAL, VILLE, TEL : ne sont plus filtrés (identifient le patient)
        ("ADRESSE", "13, Avenue de l'Interne J", "", -1, False),
        ("ADRESSE", "22 LOT MENDI ALDE", "", -1, False),
        ("CODE_POSTAL", "12345 CHICAGO CEDEX", "", -1, False),
        ("CODE_POSTAL", "12345", "", -1, False),
        ("VILLE", "CHICAGO CEDEX", "", -1, False),
        ("VILLE", "CHERAUTE", "", -1, False),
        ("VILLE", "DROIT", "", -1, False),
        ("TEL", "05 59 44 35 35", "", -1, False),
        ("TEL", "0676085336", "", -1, False),
        # EPISODE : filtré uniquement si provient du nom de fichier trackare
        ("EPISODE", "23202435", "trackare-14004105-23202435", -1, True),
        ("EPISODE", "23102610", "CRH_23102610", 0, False),
    ]
    
    print("Tests du filtre hospitalier:")
    print("=" * 80)
    
    for pii_type, text, filename, page, expected in test_cases:
        result = filter.should_filter(pii_type, text, filename, page)
        status = "✅" if result == expected else "❌"
        print(f"{status} {pii_type:15s} '{text:30s}' -> {result} (attendu: {expected})")
