"""
Tests unitaires pour le QuestionGenerator.

Ces tests vérifient:
- La génération de questions priorisées (max 5)
- La détection d'incohérences codes/faits
- La priorisation des questions par impact
"""

from datetime import datetime

import pytest

from pipeline_mco_pmsi.models.clinical import (
    ClinicalDocument,
    ClinicalFact,
    Evidence,
    Qualifier,
    Span,
    StructuredStay,
)
from pipeline_mco_pmsi.models.coding import Code, CodingProposal
from pipeline_mco_pmsi.models.metadata import ModelVersion
from pipeline_mco_pmsi.models.validation import ValidationIssue
from pipeline_mco_pmsi.validators.question_generator import QuestionGenerator


@pytest.fixture
def question_generator():
    """Crée une instance de QuestionGenerator."""
    return QuestionGenerator()


@pytest.fixture
def sample_document():
    """Crée un document clinique de test."""
    return ClinicalDocument(
        document_id="doc_001",
        document_type="cr_medical",
        content="Patient présente une gastrite aiguë confirmée par endoscopie.",
        creation_date=datetime(2024, 1, 15, 10, 30),
        author="Dr. Martin",
        priority=2,
    )


@pytest.fixture
def sample_evidence():
    """Crée une preuve de test."""
    return Evidence(
        document_id="doc_001",
        span=Span(start=20, end=35),
        text="gastrite aiguë",
        context="Patient présente une gastrite aiguë confirmée",
    )


@pytest.fixture
def sample_code(sample_evidence):
    """Crée un code de test."""
    return Code(
        code="K29.1",
        label="Gastrite aiguë",
        type="dp",
        evidence=[sample_evidence],
        confidence=0.85,
        reasoning="Diagnostic principal confirmé par endoscopie",
        referentiel_version="2026",
    )


@pytest.fixture
def sample_proposal(sample_code):
    """Crée une proposition de codage de test."""
    return CodingProposal(
        stay_id="stay_001",
        dp=sample_code,
        dr=None,
        das=[],
        ccam=[],
        reasoning="Séjour pour gastrite aiguë",
        model_version=ModelVersion(
            model_name="test-model",
            model_tag="v1.0",
            model_digest="a" * 64,
        ),
        prompt_version="v1.0",
    )


@pytest.fixture
def sample_fact(sample_evidence):
    """Crée un fait clinique de test."""
    return ClinicalFact(
        fact_id="fact_001",
        type="diagnostic",
        text="gastrite aiguë",
        qualifier=Qualifier(
            certainty="affirmé",
            markers=[],
            confidence=0.9,
        ),
        temporality="actuel",
        evidence=sample_evidence,
        confidence=0.9,
    )


@pytest.fixture
def sample_stay(sample_document, sample_fact):
    """Crée un séjour structuré de test."""
    return StructuredStay(
        stay_id="stay_001",
        documents=[sample_document],
        sections=[],
        facts=[sample_fact],
    )


class TestQuestionGeneratorBasic:
    """Tests de base pour le QuestionGenerator."""

    def test_initialization(self):
        """Test l'initialisation du générateur."""
        generator = QuestionGenerator()
        assert generator.MAX_QUESTIONS == 5

    def test_generate_questions_returns_list(
        self, question_generator, sample_proposal, sample_stay
    ):
        """Test que generate_questions retourne une liste."""
        questions = question_generator.generate_questions(
            sample_proposal, sample_stay, []
        )
        assert isinstance(questions, list)

    def test_generate_questions_respects_max_limit(
        self, question_generator, sample_proposal, sample_stay
    ):
        """Test que le nombre de questions ne dépasse pas MAX_QUESTIONS."""
        # Créer beaucoup de problèmes de validation
        many_issues = [
            ValidationIssue(
                issue_id=f"i{i}",
                severity="bloquant",
                category="missing_info",
                message=f"Problème {i}",
                affected_codes=[],
                suggested_action=f"Action {i}",
            )
            for i in range(10)
        ]

        questions = question_generator.generate_questions(
            sample_proposal, sample_stay, many_issues
        )

        assert len(questions) <= QuestionGenerator.MAX_QUESTIONS


class TestQuestionGeneration:
    """Tests pour la génération de questions."""

    def test_generate_from_blocking_issues(
        self, question_generator, sample_proposal, sample_stay
    ):
        """Test la génération de questions depuis des problèmes bloquants."""
        blocking_issue = ValidationIssue(
            issue_id="i1",
            severity="bloquant",
            category="missing_info",
            message="DP manquant",
            affected_codes=[],
            suggested_action="Ajouter un DP",
        )

        questions = question_generator.generate_questions(
            sample_proposal, sample_stay, [blocking_issue]
        )

        # Vérifier qu'une question est générée
        assert len(questions) > 0
        # Vérifier que la question a une priorité haute (1)
        assert any(q.priority == 1 for q in questions)

    def test_generate_from_review_issues(
        self, question_generator, sample_proposal, sample_stay
    ):
        """Test la génération de questions depuis des problèmes à revoir."""
        review_issue = ValidationIssue(
            issue_id="i1",
            severity="a_revoir",
            category="contradiction",
            message="Incohérence détectée",
            affected_codes=["K29.1"],
            suggested_action="Vérifier le code",
        )

        questions = question_generator.generate_questions(
            sample_proposal, sample_stay, [review_issue]
        )

        # Vérifier qu'une question est générée
        assert len(questions) > 0
        # Vérifier que la question a une priorité moyenne (2)
        assert any(q.priority == 2 for q in questions)

    def test_no_questions_from_info_issues(
        self, question_generator, sample_proposal, sample_stay
    ):
        """Test qu'aucune question n'est générée depuis des problèmes info."""
        info_issue = ValidationIssue(
            issue_id="i1",
            severity="info",
            category="other",
            message="Information",
            affected_codes=[],
            suggested_action="Aucune",
        )

        questions = question_generator.generate_questions(
            sample_proposal, sample_stay, [info_issue]
        )

        # Les questions info ne génèrent pas de questions
        # (mais d'autres sources peuvent en générer)
        # Donc on vérifie juste que ça ne plante pas
        assert isinstance(questions, list)

    def test_generate_for_suspected_facts(
        self, question_generator, sample_proposal, sample_document
    ):
        """Test la génération de questions pour faits suspectés."""
        suspected_fact = ClinicalFact(
            fact_id="fact_001",
            type="diagnostic",
            text="gastrite",
            qualifier=Qualifier(
                certainty="suspecté",
                markers=["possible"],
                confidence=0.7,
            ),
            temporality="actuel",
            evidence=Evidence(
                document_id="doc_001",
                span=Span(start=20, end=35),
                text="possible gastrite",
                context="Patient présente une possible gastrite",
            ),
            confidence=0.7,
        )

        stay = StructuredStay(
            stay_id="stay_001",
            documents=[sample_document],
            sections=[],
            facts=[suspected_fact],
        )

        questions = question_generator.generate_questions(
            sample_proposal, stay, []
        )

        # Vérifier qu'une question est générée pour le fait suspecté
        suspected_questions = [
            q for q in questions
            if "suspecté" in q.text.lower() or "confirmé" in q.text.lower()
        ]
        assert len(suspected_questions) > 0
        assert suspected_questions[0].category == "clarification"

    def test_generate_for_low_confidence_codes(
        self, question_generator, sample_stay
    ):
        """Test la génération de questions pour codes à faible confiance."""
        low_confidence_code = Code(
            code="K29.1",
            label="Gastrite",
            type="dp",
            evidence=[sample_stay.facts[0].evidence],
            confidence=0.5,  # Confiance faible
            reasoning="Diagnostic incertain",
            referentiel_version="2026",
        )

        proposal = CodingProposal(
            stay_id="stay_001",
            dp=low_confidence_code,
            dr=None,
            das=[],
            ccam=[],
            reasoning="Test",
            model_version=ModelVersion(
                model_name="test", model_tag="v1", model_digest="a" * 64
            ),
            prompt_version="v1",
        )

        questions = question_generator.generate_questions(
            proposal, sample_stay, []
        )

        # Vérifier qu'une question est générée pour la faible confiance
        confidence_questions = [
            q for q in questions
            if "confiance" in q.text.lower() or "confirmer" in q.text.lower()
        ]
        assert len(confidence_questions) > 0


class TestInconsistencyDetection:
    """Tests pour la détection d'incohérences."""

    def test_detect_negated_fact_with_code(
        self, question_generator, sample_document
    ):
        """Test la détection d'un fait nié avec code proposé."""
        negated_fact = ClinicalFact(
            fact_id="fact_001",
            type="diagnostic",
            text="gastrite",
            qualifier=Qualifier(
                certainty="nié",
                markers=["pas de"],
                confidence=0.9,
            ),
            temporality="actuel",
            evidence=Evidence(
                document_id="doc_001",
                span=Span(start=20, end=35),
                text="pas de gastrite",
                context="Patient ne présente pas de gastrite",
            ),
            confidence=0.9,
        )

        stay = StructuredStay(
            stay_id="stay_001",
            documents=[sample_document],
            sections=[],
            facts=[negated_fact],
        )

        # Code pour le diagnostic nié
        code = Code(
            code="K29.1",
            label="Gastrite",
            type="dp",
            evidence=[negated_fact.evidence],
            confidence=0.8,
            reasoning="Test",
            referentiel_version="2026",
        )

        proposal = CodingProposal(
            stay_id="stay_001",
            dp=code,
            dr=None,
            das=[],
            ccam=[],
            reasoning="Test",
            model_version=ModelVersion(
                model_name="test", model_tag="v1", model_digest="a" * 64
            ),
            prompt_version="v1",
        )

        questions = question_generator.generate_questions(
            proposal, stay, []
        )

        # Vérifier qu'une question d'incohérence est générée
        inconsistency_questions = [
            q for q in questions
            if q.category == "contradiction" and "nié" in q.text.lower()
        ]
        assert len(inconsistency_questions) > 0
        # Vérifier que la priorité est haute (1)
        assert inconsistency_questions[0].priority == 1

    def test_detect_document_contradictions(
        self, question_generator, sample_proposal
    ):
        """Test la détection de contradictions entre documents."""
        # Créer deux faits contradictoires dans différents documents
        fact1 = ClinicalFact(
            fact_id="fact_001",
            type="diagnostic",
            text="gastrite",
            qualifier=Qualifier(
                certainty="affirmé",
                markers=[],
                confidence=0.9,
            ),
            temporality="actuel",
            evidence=Evidence(
                document_id="doc_001",
                span=Span(start=20, end=35),
                text="gastrite confirmée",
                context="Patient présente une gastrite confirmée",
            ),
            confidence=0.9,
        )

        fact2 = ClinicalFact(
            fact_id="fact_002",
            type="diagnostic",
            text="gastrite",
            qualifier=Qualifier(
                certainty="nié",
                markers=["pas de"],
                confidence=0.9,
            ),
            temporality="actuel",
            evidence=Evidence(
                document_id="doc_002",
                span=Span(start=10, end=25),
                text="pas de gastrite",
                context="Examen ne montre pas de gastrite",
            ),
            confidence=0.9,
        )

        doc1 = ClinicalDocument(
            document_id="doc_001",
            document_type="cr_medical",
            content="Test",
            creation_date=datetime(2024, 1, 15),
            priority=2,
        )

        doc2 = ClinicalDocument(
            document_id="doc_002",
            document_type="imagerie",
            content="Test",
            creation_date=datetime(2024, 1, 16),
            priority=3,
        )

        stay = StructuredStay(
            stay_id="stay_001",
            documents=[doc1, doc2],
            sections=[],
            facts=[fact1, fact2],
        )

        questions = question_generator.generate_questions(
            sample_proposal, stay, []
        )

        # Vérifier qu'une question de contradiction est générée
        contradiction_questions = [
            q for q in questions
            if q.category == "contradiction" and "contradiction" in q.text.lower()
        ]
        assert len(contradiction_questions) > 0


class TestQuestionPrioritization:
    """Tests pour la priorisation des questions."""

    def test_questions_sorted_by_priority(self, question_generator):
        """Test que les questions sont triées par priorité."""
        # Créer des questions avec différentes priorités
        from pipeline_mco_pmsi.models.validation import Question

        questions = [
            Question(
                question_id="q1",
                text="Question priorité 3",
                priority=3,
                category="confirmation",
                context="Test",
                suggested_answers=[],
            ),
            Question(
                question_id="q2",
                text="Question priorité 1",
                priority=1,
                category="contradiction",
                context="Test",
                suggested_answers=[],
            ),
            Question(
                question_id="q3",
                text="Question priorité 2",
                priority=2,
                category="clarification",
                context="Test",
                suggested_answers=[],
            ),
        ]

        # Utiliser la méthode de priorisation
        sorted_questions = question_generator._prioritize_and_limit(questions)

        # Vérifier que les questions sont triées par priorité
        priorities = [q.priority for q in sorted_questions]
        assert priorities == sorted(priorities)
        assert sorted_questions[0].priority == 1

    def test_contradiction_category_prioritized(self, question_generator):
        """Test que les contradictions sont priorisées."""
        from pipeline_mco_pmsi.models.validation import Question

        questions = [
            Question(
                question_id="q1",
                text="Question confirmation",
                priority=2,
                category="confirmation",
                context="Test",
                suggested_answers=[],
            ),
            Question(
                question_id="q2",
                text="Question contradiction",
                priority=2,
                category="contradiction",
                context="Test",
                suggested_answers=[],
            ),
        ]

        sorted_questions = question_generator._prioritize_and_limit(questions)

        # Avec la même priorité, la contradiction doit venir en premier
        assert sorted_questions[0].category == "contradiction"

    def test_max_questions_limit_enforced(self, question_generator):
        """Test que la limite MAX_QUESTIONS est respectée."""
        from pipeline_mco_pmsi.models.validation import Question

        # Créer plus de MAX_QUESTIONS questions
        many_questions = [
            Question(
                question_id=f"q{i}",
                text=f"Question {i}",
                priority=i % 5 + 1,
                category="confirmation",
                context="Test",
                suggested_answers=[],
            )
            for i in range(10)
        ]

        limited_questions = question_generator._prioritize_and_limit(many_questions)

        # Vérifier que seulement MAX_QUESTIONS sont retournées
        assert len(limited_questions) == QuestionGenerator.MAX_QUESTIONS
        # Vérifier que ce sont les plus prioritaires
        assert all(q.priority <= 3 for q in limited_questions)
