"""
Générateur de questions pour informations manquantes.

Ce module implémente la génération de questions qui:
- Génère des questions priorisées (maximum 5)
- Détecte les incohérences entre codes et faits cliniques
- Priorise les questions par impact sur la qualité du codage

Exigences: 9.3, 9.4
"""

import logging
import uuid
from typing import List, Set

from pipeline_mco_pmsi.models.clinical import ClinicalFact, StructuredStay
from pipeline_mco_pmsi.models.coding import Code, CodingProposal
from pipeline_mco_pmsi.models.validation import Question, ValidationIssue

logger = logging.getLogger(__name__)


class QuestionGenerator:
    """
    Générateur de questions pour informations manquantes.

    Responsabilités:
    - Générer des questions priorisées (max 5)
    - Détecter les incohérences codes/faits
    - Prioriser les questions par impact sur le codage

    Exigences: 9.3, 9.4
    """

    MAX_QUESTIONS = 5

    def __init__(self):
        """Initialise le générateur de questions."""
        logger.info("QuestionGenerator initialisé")

    def generate_questions(
        self,
        proposal: CodingProposal,
        structured_stay: StructuredStay,
        validation_issues: List[ValidationIssue],
    ) -> List[Question]:
        """
        Génère des questions priorisées pour informations manquantes.

        Args:
            proposal: Proposition de codage
            structured_stay: Séjour structuré avec faits cliniques
            validation_issues: Problèmes de validation détectés

        Returns:
            Liste de questions (maximum 5) triées par priorité

        Exigences: 9.3, 9.4
        """
        logger.info(f"Génération de questions pour le séjour {proposal.stay_id}")

        questions: List[Question] = []

        # 1. Générer des questions à partir des problèmes de validation
        validation_questions = self._generate_from_validation_issues(validation_issues)
        questions.extend(validation_questions)

        # 2. Générer des questions pour les faits suspectés
        suspected_questions = self._generate_for_suspected_facts(structured_stay)
        questions.extend(suspected_questions)

        # 3. Générer des questions pour les incohérences
        inconsistency_questions = self._detect_inconsistencies(
            proposal, structured_stay
        )
        questions.extend(inconsistency_questions)

        # 4. Générer des questions pour les informations manquantes
        missing_info_questions = self._generate_for_missing_info(
            proposal, structured_stay
        )
        questions.extend(missing_info_questions)

        # 5. Trier par priorité et limiter à MAX_QUESTIONS
        questions = self._prioritize_and_limit(questions)

        logger.info(
            f"Génération terminée: {len(questions)} questions "
            f"(priorité moyenne: {self._average_priority(questions):.1f})"
        )

        return questions

    def _generate_from_validation_issues(
        self,
        validation_issues: List[ValidationIssue],
    ) -> List[Question]:
        """
        Génère des questions à partir des problèmes de validation.

        Args:
            validation_issues: Problèmes de validation détectés

        Returns:
            Liste de questions
        """
        questions: List[Question] = []

        for issue in validation_issues:
            # Générer une question seulement pour les problèmes bloquants et à revoir
            if issue.severity not in ["bloquant", "a_revoir"]:
                continue

            # Déterminer la priorité selon la sévérité
            priority = 1 if issue.severity == "bloquant" else 2

            # Construire le texte de la question
            if issue.category == "missing_info":
                question_text = f"Information manquante: {issue.message}"
            elif issue.category == "contradiction":
                question_text = f"Incohérence détectée: {issue.message}"
            elif issue.category == "eligibility":
                question_text = f"Éligibilité à vérifier: {issue.message}"
            else:
                question_text = issue.message

            question = Question(
                question_id=self._generate_question_id(),
                text=question_text,
                priority=priority,
                category="missing_info" if issue.category == "missing_info" else "clarification",
                context=issue.suggested_action,
                suggested_answers=[],
            )
            questions.append(question)

        logger.debug(f"Généré {len(questions)} questions depuis les problèmes de validation")
        return questions

    def _generate_for_suspected_facts(
        self,
        structured_stay: StructuredStay,
    ) -> List[Question]:
        """
        Génère des questions pour les faits cliniques suspectés.

        Les faits suspectés nécessitent une clarification pour déterminer
        s'ils doivent être codés ou non.

        Args:
            structured_stay: Séjour structuré

        Returns:
            Liste de questions
        """
        questions: List[Question] = []

        suspected_facts = [
            f for f in structured_stay.facts
            if f.qualifier.certainty == "suspecté"
        ]

        for fact in suspected_facts:
            question = Question(
                question_id=self._generate_question_id(),
                text=f"Le diagnostic '{fact.text}' est suspecté. A-t-il été confirmé?",
                priority=2,  # Priorité moyenne
                category="clarification",
                context=f"Fait extrait du document {fact.evidence.document_id}",
                suggested_answers=["Confirmé", "Non confirmé", "Nécessite examens complémentaires"],
            )
            questions.append(question)

        logger.debug(f"Généré {len(questions)} questions pour faits suspectés")
        return questions

    def _detect_inconsistencies(
        self,
        proposal: CodingProposal,
        structured_stay: StructuredStay,
    ) -> List[Question]:
        """
        Détecte les incohérences entre codes proposés et faits cliniques.

        Incohérences détectées:
        - Codes sans faits correspondants
        - Faits niés mais codes proposés
        - Contradictions entre documents

        Args:
            proposal: Proposition de codage
            structured_stay: Séjour structuré

        Returns:
            Liste de questions sur les incohérences

        Exigences: 9.4
        """
        questions: List[Question] = []

        # Détecter les faits niés avec codes proposés
        negated_facts = [
            f for f in structured_stay.facts
            if f.qualifier.certainty == "nié"
        ]

        all_codes = []
        if proposal.dp:
            all_codes.append(proposal.dp)
        if proposal.dr:
            all_codes.append(proposal.dr)
        all_codes.extend(proposal.das)
        all_codes.extend(proposal.ccam)

        for fact in negated_facts:
            # Vérifier si un code correspond à ce fait nié
            for code in all_codes:
                # Comparaison simple sur le texte (pourrait être améliorée)
                if any(fact.text.lower() in evidence.text.lower() for evidence in code.evidence):
                    question = Question(
                        question_id=self._generate_question_id(),
                        text=f"Le diagnostic '{fact.text}' est nié dans les documents mais le code {code.code} est proposé. Est-ce correct?",
                        priority=1,  # Haute priorité (incohérence critique)
                        category="contradiction",
                        context=f"Fait nié: {fact.text}, Code proposé: {code.code} ({code.label})",
                        suggested_answers=["Code incorrect, à retirer", "Code correct malgré la négation", "Nécessite clarification"],
                    )
                    questions.append(question)
                    logger.warning(
                        f"Incohérence détectée: fait nié '{fact.text}' "
                        f"mais code {code.code} proposé"
                    )

        # Détecter les contradictions entre documents
        contradictions = self._detect_document_contradictions(structured_stay)
        for contradiction_text in contradictions:
            question = Question(
                question_id=self._generate_question_id(),
                text=f"Contradiction entre documents: {contradiction_text}",
                priority=2,
                category="contradiction",
                context="Informations contradictoires trouvées dans différents documents",
                suggested_answers=["Prioriser document A", "Prioriser document B", "Nécessite arbitrage médical"],
            )
            questions.append(question)

        logger.debug(f"Généré {len(questions)} questions pour incohérences")
        return questions

    def _detect_document_contradictions(
        self,
        structured_stay: StructuredStay,
    ) -> List[str]:
        """
        Détecte les contradictions entre documents.

        Recherche des faits similaires avec qualificateurs différents
        dans différents documents.

        Args:
            structured_stay: Séjour structuré

        Returns:
            Liste de textes décrivant les contradictions
        """
        contradictions: List[str] = []

        # Grouper les faits par texte similaire
        facts_by_text: dict[str, List[ClinicalFact]] = {}
        for fact in structured_stay.facts:
            text_key = fact.text.lower().strip()
            if text_key not in facts_by_text:
                facts_by_text[text_key] = []
            facts_by_text[text_key].append(fact)

        # Détecter les contradictions
        for text, facts in facts_by_text.items():
            if len(facts) < 2:
                continue

            # Vérifier si les qualificateurs diffèrent
            certainties = set(f.qualifier.certainty for f in facts)
            if len(certainties) > 1:
                doc_ids = [f.evidence.document_id for f in facts]
                contradiction = (
                    f"'{text}' a des qualificateurs différents dans les documents "
                    f"{', '.join(set(doc_ids))}: {', '.join(certainties)}"
                )
                contradictions.append(contradiction)
                logger.info(f"Contradiction détectée: {contradiction}")

        return contradictions

    def _generate_for_missing_info(
        self,
        proposal: CodingProposal,
        structured_stay: StructuredStay,
    ) -> List[Question]:
        """
        Génère des questions pour les informations manquantes.

        Args:
            proposal: Proposition de codage
            structured_stay: Séjour structuré

        Returns:
            Liste de questions
        """
        questions: List[Question] = []

        # Vérifier les dates de réalisation CCAM manquantes
        for ccam_code in proposal.ccam:
            # Note: La vérification de la date de réalisation est faite par le GroupageValidator
            # Ici on génère une question si la confiance est faible
            if ccam_code.confidence < 0.6:
                question = Question(
                    question_id=self._generate_question_id(),
                    text=f"L'acte {ccam_code.code} ({ccam_code.label}) a une confiance faible. Confirmer la réalisation?",
                    priority=3,
                    category="confirmation",
                    context=f"Confiance: {ccam_code.confidence:.2f}",
                    suggested_answers=["Acte confirmé", "Acte non réalisé", "Nécessite vérification"],
                )
                questions.append(question)

        # Vérifier les codes avec confiance faible
        all_codes = []
        if proposal.dp and proposal.dp.confidence < 0.6:
            all_codes.append(("DP", proposal.dp))
        if proposal.dr and proposal.dr.confidence < 0.6:
            all_codes.append(("DR", proposal.dr))
        for das in proposal.das:
            if das.confidence < 0.6:
                all_codes.append(("DAS", das))

        for code_type, code in all_codes:
            question = Question(
                question_id=self._generate_question_id(),
                text=f"Le {code_type} {code.code} ({code.label}) a une confiance faible. Confirmer le diagnostic?",
                priority=2 if code_type == "DP" else 3,
                category="confirmation",
                context=f"Confiance: {code.confidence:.2f}. Raisonnement: {code.reasoning[:100]}...",
                suggested_answers=["Diagnostic confirmé", "Diagnostic incorrect", "Nécessite examens complémentaires"],
            )
            questions.append(question)

        logger.debug(f"Généré {len(questions)} questions pour informations manquantes")
        return questions

    def _prioritize_and_limit(
        self,
        questions: List[Question],
    ) -> List[Question]:
        """
        Priorise les questions et limite à MAX_QUESTIONS.

        Critères de priorisation:
        1. Priorité (1 = haute, 5 = basse)
        2. Catégorie (contradiction > missing_info > clarification > confirmation)

        Args:
            questions: Liste de questions non triées

        Returns:
            Liste de questions triées et limitées à MAX_QUESTIONS

        Exigences: 9.3
        """
        # Définir l'ordre de priorité des catégories
        category_priority = {
            "contradiction": 1,
            "missing_info": 2,
            "clarification": 3,
            "confirmation": 4,
        }

        # Trier par priorité puis par catégorie
        sorted_questions = sorted(
            questions,
            key=lambda q: (q.priority, category_priority.get(q.category, 5)),
        )

        # Limiter à MAX_QUESTIONS
        limited_questions = sorted_questions[:self.MAX_QUESTIONS]

        if len(questions) > self.MAX_QUESTIONS:
            logger.info(
                f"Questions limitées de {len(questions)} à {self.MAX_QUESTIONS} "
                f"(priorité minimale conservée: {limited_questions[-1].priority})"
            )

        return limited_questions

    def _average_priority(self, questions: List[Question]) -> float:
        """
        Calcule la priorité moyenne des questions.

        Args:
            questions: Liste de questions

        Returns:
            Priorité moyenne (1.0 à 5.0)
        """
        if not questions:
            return 0.0
        return sum(q.priority for q in questions) / len(questions)

    def _generate_question_id(self) -> str:
        """
        Génère un identifiant unique pour une question.

        Returns:
            Identifiant unique (UUID)
        """
        return f"q_{uuid.uuid4().hex[:12]}"
