"""
Tests pour le Pipeline principal.

Ce module teste l'orchestration complète du pipeline de codage MCO PMSI.
"""

import pytest
from datetime import datetime, timedelta

from pipeline_mco_pmsi.models.clinical import ClinicalDocument
from pipeline_mco_pmsi.models.metadata import StayMetadata
from pipeline_mco_pmsi.pipeline import Pipeline, PipelineResult
from pipeline_mco_pmsi.database.models import StayDB


def create_stay_in_db(db_session, stay_metadata: StayMetadata) -> None:
    """Helper pour créer un Stay dans la base de données."""
    stay_db = StayDB(
        stay_id=stay_metadata.stay_id,
        admission_date=stay_metadata.admission_date,
        discharge_date=stay_metadata.discharge_date,
        specialty=stay_metadata.specialty,
        unit=stay_metadata.unit,
        age=stay_metadata.age,
        sex=stay_metadata.sex,
    )
    db_session.add(stay_db)
    db_session.commit()


def test_pipeline_initialization(db_session, rag_engine):
    """Test l'initialisation du pipeline avec tous ses composants."""
    pipeline = Pipeline(
        db_session=db_session,
        rag_engine=rag_engine,
        model_name="mock-llm",
        model_version="1.0.0",
        codeur_prompt_version="codeur-1.0.0",
        verificateur_prompt_version="verificateur-1.0.0",
        groupage_version="2026",
        rules_version="1.0.0",
        conservative_mode=True,
    )

    assert pipeline is not None
    assert pipeline.document_processor is not None
    assert pipeline.pii_protector is not None
    assert pipeline.clinical_facts_extractor is not None
    assert pipeline.codeur is not None
    assert pipeline.verificateur is not None
    assert pipeline.groupage_validator is not None
    assert pipeline.pmsi_validator is not None
    assert pipeline.question_generator is not None
    assert pipeline.audit_logger is not None


def test_pipeline_process_stay_simple(db_session, rag_engine, sample_stay):
    """Test le traitement complet d'un séjour simple."""
    pipeline = Pipeline(
        db_session=db_session,
        rag_engine=rag_engine,
    )

    # Créer un document clinique simple
    documents = [
        ClinicalDocument(
            document_id="doc_001",
            document_type="cr_medical",
            content="""
            Compte Rendu Médical
            
            Diagnostic: Gastrite aiguë
            
            Le patient présente une gastrite aiguë confirmée par endoscopie.
            Traitement par IPP prescrit.
            """,
            creation_date=datetime.now(),
            author="Dr. Martin",
            priority=2,
        )
    ]

    # Métadonnées du séjour
    stay_metadata = StayMetadata(
        stay_id="stay_001",
        admission_date=datetime.now() - timedelta(days=2),
        discharge_date=datetime.now(),
        specialty="gastro-enterologie",
        unit="medecine",
        age=45,
        sex="M",
    )
    
    # Créer le Stay dans la base de données
    create_stay_in_db(db_session, stay_metadata)

    # Traiter le séjour
    result = pipeline.process_stay(documents, stay_metadata)

    # Vérifications
    assert result is not None
    assert result.success is True
    assert result.stay_id == "stay_001"
    assert result.structured_stay is not None
    assert len(result.structured_stay.documents) == 1
    assert len(result.structured_stay.sections) > 0
    assert len(result.structured_stay.facts) > 0
    assert result.coding_proposal is not None
    assert result.verification_result is not None
    assert result.groupage_result is not None
    assert result.versions is not None


def test_pipeline_process_stay_with_negation(db_session, rag_engine):
    """Test le traitement d'un séjour avec diagnostic nié."""
    pipeline = Pipeline(
        db_session=db_session,
        rag_engine=rag_engine,
        conservative_mode=True,
    )

    # Document avec diagnostic nié
    documents = [
        ClinicalDocument(
            document_id="doc_002",
            document_type="cr_medical",
            content="""
            Compte Rendu Médical
            
            Diagnostic: Pas de gastrite
            
            Le patient ne présente pas de gastrite à l'endoscopie.
            Absence de lésion muqueuse.
            """,
            creation_date=datetime.now(),
            author="Dr. Dupont",
            priority=2,
        )
    ]

    stay_metadata = StayMetadata(
        stay_id="stay_002",
        admission_date=datetime.now() - timedelta(days=1),
        discharge_date=datetime.now(),
        specialty="gastro-enterologie",
        unit="medecine",
        age=50,
        sex="F",
    )
    
    # Créer le Stay dans la base de données
    create_stay_in_db(db_session, stay_metadata)

    # Traiter le séjour
    result = pipeline.process_stay(documents, stay_metadata)

    # Vérifications
    assert result.success is True
    
    # En mode conservateur, les diagnostics niés ne doivent pas être codés
    if result.coding_proposal and result.coding_proposal.dp:
        # Si un DP est proposé, il ne doit pas être basé sur le fait nié
        for evidence in result.coding_proposal.dp.evidence:
            assert "pas de gastrite" not in evidence.text.lower()


def test_pipeline_process_stay_multi_documents(db_session, rag_engine):
    """Test le traitement d'un séjour avec plusieurs documents."""
    pipeline = Pipeline(
        db_session=db_session,
        rag_engine=rag_engine,
    )

    # Plusieurs documents avec priorités différentes
    documents = [
        ClinicalDocument(
            document_id="doc_003",
            document_type="courrier",
            content="Courrier de sortie: Patient traité pour gastrite.",
            creation_date=datetime.now(),
            author="Dr. Martin",
            priority=5,  # Basse priorité
        ),
        ClinicalDocument(
            document_id="doc_004",
            document_type="cr_medical",
            content="""
            Compte Rendu Médical
            
            Diagnostic: Gastrite aiguë hémorragique
            
            Le patient présente une gastrite aiguë hémorragique.
            Endoscopie réalisée.
            """,
            creation_date=datetime.now(),
            author="Dr. Dupont",
            priority=2,  # Haute priorité
        ),
    ]

    stay_metadata = StayMetadata(
        stay_id="stay_003",
        admission_date=datetime.now() - timedelta(days=3),
        discharge_date=datetime.now(),
        specialty="gastro-enterologie",
        unit="medecine",
        age=55,
        sex="M",
    )
    
    # Créer le Stay dans la base de données
    create_stay_in_db(db_session, stay_metadata)

    # Traiter le séjour
    result = pipeline.process_stay(documents, stay_metadata)

    # Vérifications
    assert result.success is True
    assert len(result.structured_stay.documents) == 2
    
    # Vérifier que les documents sont triés par priorité
    assert result.structured_stay.documents[0].priority <= result.structured_stay.documents[1].priority


def test_pipeline_result_can_auto_validate(db_session, rag_engine):
    """Test la détermination de la possibilité de validation automatique."""
    pipeline = Pipeline(
        db_session=db_session,
        rag_engine=rag_engine,
    )

    documents = [
        ClinicalDocument(
            document_id="doc_005",
            document_type="cr_medical",
            content="Diagnostic: Gastrite aiguë confirmée.",
            creation_date=datetime.now(),
            author="Dr. Martin",
            priority=2,
        )
    ]

    stay_metadata = StayMetadata(
        stay_id="stay_004",
        admission_date=datetime.now() - timedelta(days=1),
        discharge_date=datetime.now(),
        specialty="gastro-enterologie",
        unit="medecine",
        age=40,
        sex="F",
    )

    result = pipeline.process_stay(documents, stay_metadata)

    # Vérifier la logique de validation automatique
    if result.success and result.verification_result:
        if result.verification_result.decision == "accept" and not result.has_blocking_issues():
            assert result.can_auto_validate() is True
        else:
            assert result.can_auto_validate() is False


def test_pipeline_export_audit_trail(db_session, rag_engine):
    """Test l'export de la piste d'audit."""
    pipeline = Pipeline(
        db_session=db_session,
        rag_engine=rag_engine,
    )

    documents = [
        ClinicalDocument(
            document_id="doc_006",
            document_type="cr_medical",
            content="Diagnostic: Gastrite aiguë.",
            creation_date=datetime.now(),
            author="Dr. Dupont",
            priority=2,
        )
    ]

    stay_metadata = StayMetadata(
        stay_id="stay_005",
        admission_date=datetime.now() - timedelta(days=1),
        discharge_date=datetime.now(),
        specialty="gastro-enterologie",
        unit="medecine",
        age=55,
        sex="M",
    )
    
    # Créer le Stay dans la base de données
    create_stay_in_db(db_session, stay_metadata)

    # Traiter le séjour
    result = pipeline.process_stay(documents, stay_metadata)
    assert result.success is True

    # Exporter l'audit
    audit_dict = pipeline.export_audit_trail("stay_005", include_pii=False)

    # Vérifications
    assert audit_dict is not None
    assert "stay_id" in audit_dict
    assert audit_dict["stay_id"] == "stay_005"
    assert "documents" in audit_dict
    assert "facts" in audit_dict
    assert "coding_proposal" in audit_dict
    assert "verification_result" in audit_dict
    assert "audit_records" in audit_dict
    assert "versions" in audit_dict


def test_pipeline_error_handling(db_session, rag_engine):
    """Test la gestion des erreurs du pipeline."""
    pipeline = Pipeline(
        db_session=db_session,
        rag_engine=rag_engine,
    )

    # Documents vides (devrait causer une erreur)
    documents = []

    stay_metadata = StayMetadata(
        stay_id="stay_006",
        admission_date=datetime.now() - timedelta(days=1),
        discharge_date=datetime.now(),
        specialty="gastro-enterologie",
        unit="medecine",
        age=45,
        sex="M",
    )
    
    # Créer le Stay dans la base de données
    create_stay_in_db(db_session, stay_metadata)

    # Traiter le séjour (devrait échouer gracieusement)
    result = pipeline.process_stay(documents, stay_metadata)

    # Vérifications
    assert result is not None
    assert result.success is False
    assert result.error_message is not None
    # Note: avec des documents vides, le pipeline échoue avant de créer les propositions
    # donc coding_proposal et verification_result peuvent être None


def test_pipeline_version_info(db_session, rag_engine):
    """Test la construction des informations de version."""
    pipeline = Pipeline(
        db_session=db_session,
        rag_engine=rag_engine,
        model_name="test-model",
        model_version="2.0.0",
        codeur_prompt_version="codeur-2.0.0",
        verificateur_prompt_version="verificateur-2.0.0",
        groupage_version="2026",
        rules_version="2.0.0",
    )

    documents = [
        ClinicalDocument(
            document_id="doc_007",
            document_type="cr_medical",
            content="Diagnostic: Gastrite aiguë.",
            creation_date=datetime.now(),
            author="Dr. Martin",
            priority=2,
        )
    ]

    stay_metadata = StayMetadata(
        stay_id="stay_007",
        admission_date=datetime.now() - timedelta(days=1),
        discharge_date=datetime.now(),
        specialty="gastro-enterologie",
        unit="medecine",
        age=50,
        sex="F",
    )

    result = pipeline.process_stay(documents, stay_metadata)

    # Vérifier les informations de version
    assert result.versions is not None
    assert result.versions.model_name == "test-model"
    assert result.versions.model_tag == "2.0.0"
    assert result.versions.model_digest is not None
    assert "codeur=codeur-2.0.0" in result.versions.prompt_version
    assert "verificateur=verificateur-2.0.0" in result.versions.prompt_version
    assert result.versions.groupage_version == "2026"
    assert result.versions.rules_version == "2.0.0"
    assert result.versions.inference_params is not None


def test_pipeline_multi_document_contradictions(db_session, rag_engine):
    """
    Test la détection de contradictions entre documents multiples.
    
    Exigences: 15.4, 15.5
    """
    from pipeline_mco_pmsi.database.models import StayDB
    from pipeline_mco_pmsi.models.clinical import Qualifier, Span
    from unittest.mock import patch
    
    pipeline = Pipeline(
        db_session=db_session,
        rag_engine=rag_engine,
    )

    # Créer le séjour dans la base de données
    stay = StayDB(
        stay_id="stay_contradiction_001",
        admission_date=datetime.now() - timedelta(days=1),
        discharge_date=datetime.now(),
        specialty="chirurgie",
        unit="urgences",
        age=35,
        sex="M",
    )
    db_session.add(stay)
    db_session.commit()

    # Documents avec informations contradictoires
    documents = [
        ClinicalDocument(
            document_id="doc_cro_001",
            document_type="cr_operatoire",
            content="""
            Compte Rendu Opératoire
            
            Diagnostic: Appendicite aiguë
            
            Le patient présente une appendicite aiguë confirmée.
            Appendicectomie réalisée.
            """,
            creation_date=datetime.now(),
            author="Dr. Chirurgien",
            priority=1,  # Haute priorité (CRO)
        ),
        ClinicalDocument(
            document_id="doc_crm_001",
            document_type="cr_medical",
            content="""
            Compte Rendu Médical
            
            Diagnostic: Pas d'appendicite
            
            Le patient ne présente pas d'appendicite à l'examen clinique.
            Douleurs abdominales d'origine fonctionnelle.
            """,
            creation_date=datetime.now() - timedelta(hours=2),
            author="Dr. Urgentiste",
            priority=2,  # Priorité moyenne (CRM)
        ),
    ]

    stay_metadata = StayMetadata(
        stay_id="stay_contradiction_001",
        admission_date=datetime.now() - timedelta(days=1),
        discharge_date=datetime.now(),
        specialty="chirurgie",
        unit="urgences",
        age=35,
        sex="M",
    )

    # Mock l'extraction de faits pour créer des faits contradictoires
    def mock_extract_facts(structured_stay):
        """Crée des faits contradictoires pour le test."""
        from pipeline_mco_pmsi.models.clinical import ClinicalFact, Evidence, Qualifier, Span
        
        # Fait affirmé depuis le CRO
        fact_affirmed = ClinicalFact(
            fact_id="fact_001",
            type="diagnostic",
            text="appendicite aiguë",
            qualifier=Qualifier(
                certainty="affirmé",
                markers=[],
                confidence=0.95,
            ),
            temporality="actuel",
            evidence=Evidence(
                document_id="doc_cro_001",
                span=Span(start=50, end=67),
                text="appendicite aiguë",
                context="Le patient présente une appendicite aiguë confirmée",
            ),
            confidence=0.95,
        )
        
        # Fait nié depuis le CRM - MÊME TEXTE pour être groupé
        fact_negated = ClinicalFact(
            fact_id="fact_002",
            type="diagnostic",
            text="appendicite aiguë",  # Même texte que fact_affirmed
            qualifier=Qualifier(
                certainty="nié",
                markers=["pas de"],
                confidence=0.90,
            ),
            temporality="actuel",
            evidence=Evidence(
                document_id="doc_crm_001",
                span=Span(start=40, end=60),
                text="pas d'appendicite",
                context="Le patient ne présente pas d'appendicite à l'examen",
            ),
            confidence=0.90,
        )
        
        return [fact_affirmed, fact_negated]
    
    # Patcher l'extracteur de faits
    with patch.object(pipeline.clinical_facts_extractor, 'extract_facts', side_effect=mock_extract_facts):
        # Traiter le séjour
        result = pipeline.process_stay(documents, stay_metadata)

    # Vérifications
    assert result.success is True
    assert len(result.structured_stay.documents) == 2
    
    # Vérifier qu'une contradiction a été détectée
    contradiction_issues = [
        issue for issue in result.validation_issues
        if issue.category == "contradiction"
    ]
    assert len(contradiction_issues) > 0
    
    # Vérifier que la contradiction est marquée "a_revoir"
    assert any(issue.severity == "a_revoir" for issue in contradiction_issues)
    
    # Vérifier que l'action suggérée mentionne l'arbitrage TIM
    assert any("arbitrage" in issue.suggested_action.lower() for issue in contradiction_issues)


def test_pipeline_multi_document_temporal_contradiction(db_session, rag_engine):
    """
    Test la détection de contradictions temporelles entre documents.
    
    Exigences: 15.4, 15.5
    """
    from pipeline_mco_pmsi.database.models import StayDB
    
    pipeline = Pipeline(
        db_session=db_session,
        rag_engine=rag_engine,
    )

    # Créer le séjour dans la base de données
    stay = StayDB(
        stay_id="stay_temporal_001",
        admission_date=datetime.now() - timedelta(days=2),
        discharge_date=datetime.now(),
        specialty="endocrinologie",
        unit="medecine",
        age=55,
        sex="M",
    )
    db_session.add(stay)
    db_session.commit()

    # Documents avec temporalité contradictoire
    documents = [
        ClinicalDocument(
            document_id="doc_008",
            document_type="cr_medical",
            content="""
            Compte Rendu Médical
            
            Diagnostic: Diabète de type 2
            
            Le patient présente un diabète de type 2 diagnostiqué lors de ce séjour.
            Glycémie à jeun élevée.
            """,
            creation_date=datetime.now(),
            author="Dr. Endocrinologue",
            priority=2,
        ),
        ClinicalDocument(
            document_id="doc_009",
            document_type="courrier",
            content="""
            Courrier de sortie
            
            Antécédents: Diabète de type 2 connu depuis 5 ans
            
            Le patient a un antécédent de diabète de type 2.
            Traitement par metformine poursuivi.
            """,
            creation_date=datetime.now(),
            author="Dr. Interne",
            priority=5,
        ),
    ]

    stay_metadata = StayMetadata(
        stay_id="stay_temporal_001",
        admission_date=datetime.now() - timedelta(days=2),
        discharge_date=datetime.now(),
        specialty="endocrinologie",
        unit="medecine",
        age=55,
        sex="M",
    )

    # Traiter le séjour
    result = pipeline.process_stay(documents, stay_metadata)

    # Vérifications
    assert result.success is True
    
    # Vérifier qu'une contradiction temporelle a été détectée
    temporal_contradictions = [
        issue for issue in result.validation_issues
        if issue.category == "contradiction" and "temporelle" in issue.message.lower()
    ]
    
    # Note: La détection dépend de l'extraction correcte des qualificateurs temporels
    # Si aucune contradiction n'est détectée, c'est que les faits n'ont pas été extraits
    # avec des temporalités différentes


def test_pipeline_multi_document_source_traceability(db_session, rag_engine):
    """
    Test la traçabilité des sources pour les faits multi-documents.
    
    Exigences: 15.6
    """
    from pipeline_mco_pmsi.database.models import StayDB
    
    pipeline = Pipeline(
        db_session=db_session,
        rag_engine=rag_engine,
    )

    # Créer le séjour dans la base de données
    stay = StayDB(
        stay_id="stay_trace_001",
        admission_date=datetime.now() - timedelta(days=1),
        discharge_date=datetime.now(),
        specialty="chirurgie",
        unit="urgences",
        age=60,
        sex="F",
    )
    db_session.add(stay)
    db_session.commit()

    # Plusieurs documents
    documents = [
        ClinicalDocument(
            document_id="doc_010",
            document_type="cr_operatoire",
            content="Diagnostic: Cholécystite aiguë. Cholécystectomie réalisée.",
            creation_date=datetime.now(),
            author="Dr. Chirurgien",
            priority=1,
        ),
        ClinicalDocument(
            document_id="doc_011",
            document_type="imagerie",
            content="Échographie: Vésicule biliaire distendue avec calculs.",
            creation_date=datetime.now() - timedelta(hours=3),
            author="Dr. Radiologue",
            priority=3,
        ),
        ClinicalDocument(
            document_id="doc_012",
            document_type="biologie",
            content="Biologie: Hyperleucocytose à 15000/mm3.",
            creation_date=datetime.now() - timedelta(hours=4),
            author="Laboratoire",
            priority=4,
        ),
    ]

    stay_metadata = StayMetadata(
        stay_id="stay_trace_001",
        admission_date=datetime.now() - timedelta(days=1),
        discharge_date=datetime.now(),
        specialty="chirurgie",
        unit="urgences",
        age=60,
        sex="F",
    )

    # Traiter le séjour
    result = pipeline.process_stay(documents, stay_metadata)

    # Vérifications
    assert result.success is True
    assert len(result.structured_stay.documents) == 3
    
    # Vérifier que tous les faits ont un document_id valide
    valid_document_ids = {doc.document_id for doc in documents}
    for fact in result.structured_stay.facts:
        assert fact.evidence.document_id in valid_document_ids, \
            f"Fait {fact.fact_id} a un document_id invalide: {fact.evidence.document_id}"
    
    # Vérifier qu'aucun problème d'intégrité de données n'a été détecté
    data_integrity_issues = [
        issue for issue in result.validation_issues
        if issue.category == "data_integrity"
    ]
    assert len(data_integrity_issues) == 0


def test_pipeline_multi_document_priority_ordering(db_session, rag_engine):
    """
    Test que les priorités de sources sont respectées.
    
    Exigences: 15.2
    """
    from pipeline_mco_pmsi.database.models import StayDB
    
    pipeline = Pipeline(
        db_session=db_session,
        rag_engine=rag_engine,
    )

    # Créer le séjour dans la base de données
    stay = StayDB(
        stay_id="stay_priority_001",
        admission_date=datetime.now() - timedelta(days=1),
        discharge_date=datetime.now(),
        specialty="gastro-enterologie",
        unit="medecine",
        age=50,
        sex="M",
    )
    db_session.add(stay)
    db_session.commit()

    # Documents avec différentes priorités
    documents = [
        ClinicalDocument(
            document_id="doc_courrier",
            document_type="courrier",
            content="Courrier: Gastrite.",
            creation_date=datetime.now(),
            author="Dr. A",
            priority=5,  # Basse priorité
        ),
        ClinicalDocument(
            document_id="doc_cro",
            document_type="cr_operatoire",
            content="CRO: Gastrite hémorragique.",
            creation_date=datetime.now(),
            author="Dr. B",
            priority=1,  # Haute priorité
        ),
        ClinicalDocument(
            document_id="doc_crm",
            document_type="cr_medical",
            content="CRM: Gastrite aiguë.",
            creation_date=datetime.now(),
            author="Dr. C",
            priority=2,  # Priorité moyenne
        ),
    ]

    stay_metadata = StayMetadata(
        stay_id="stay_priority_001",
        admission_date=datetime.now() - timedelta(days=1),
        discharge_date=datetime.now(),
        specialty="gastro-enterologie",
        unit="medecine",
        age=50,
        sex="M",
    )

    # Traiter le séjour
    result = pipeline.process_stay(documents, stay_metadata)

    # Vérifications
    assert result.success is True
    assert len(result.structured_stay.documents) == 3
    
    # Vérifier que les documents sont triés par priorité (1 = haute, 5 = basse)
    priorities = [doc.priority for doc in result.structured_stay.documents]
    assert priorities == sorted(priorities), \
        f"Documents non triés par priorité: {priorities}"
    
    # Le premier document doit être le CRO (priorité 1)
    assert result.structured_stay.documents[0].document_type == "cr_operatoire"
    assert result.structured_stay.documents[0].priority == 1

