"""Tests for Phenopackets v2 models."""

from __future__ import annotations

from ai4gh.specs.phenopackets.models import (
    AcmgPathogenicityClassification,
    Biosample,
    Cohort,
    Diagnosis,
    Disease,
    Evidence,
    ExternalReference,
    Family,
    GenomicInterpretation,
    Individual,
    Interpretation,
    KaryotypicSex,
    Measurement,
    MedicalAction,
    MetaData,
    OntologyClass,
    Phenopacket,
    PhenotypicFeature,
    Procedure,
    ProgressStatus,
    Sex,
    TherapeuticActionability,
    TimeElement,
    VariantInterpretation,
    VitalStatus,
)


class TestEnums:
    def test_sex(self) -> None:
        assert Sex.FEMALE.value == "FEMALE"
        assert Sex.MALE.value == "MALE"

    def test_karyotypic_sex(self) -> None:
        assert KaryotypicSex.XX.value == "XX"
        assert KaryotypicSex.XY.value == "XY"

    def test_vital_status(self) -> None:
        assert VitalStatus.ALIVE.value == "ALIVE"
        assert VitalStatus.DECEASED.value == "DECEASED"

    def test_acmg(self) -> None:
        assert AcmgPathogenicityClassification.PATHOGENIC.value == "PATHOGENIC"
        assert AcmgPathogenicityClassification.BENIGN.value == "BENIGN"

    def test_therapeutic_actionability(self) -> None:
        assert TherapeuticActionability.ACTIONABLE.value == "ACTIONABLE"

    def test_progress_status(self) -> None:
        assert ProgressStatus.SOLVED.value == "SOLVED"
        assert ProgressStatus.COMPLETED.value == "COMPLETED"


class TestOntologyClass:
    def test_basic(self) -> None:
        oc = OntologyClass(id="HP:0001250", label="Seizure")
        assert oc.id == "HP:0001250"
        assert oc.label == "Seizure"

    def test_no_label(self) -> None:
        oc = OntologyClass(id="HP:0001250")
        assert oc.label is None


class TestExternalReference:
    def test_basic(self) -> None:
        ref = ExternalReference(id="PMID:123456")
        assert ref.id == "PMID:123456"


class TestTimeElement:
    def test_age(self) -> None:
        t = TimeElement(age={"iso8601duration": "P25Y"})
        assert t.age is not None

    def test_ontology(self) -> None:
        t = TimeElement(ontology_class=OntologyClass(id="HP:0011463", label="Childhood onset"))
        assert t.ontology_class is not None


class TestPhenotypicFeature:
    def test_basic(self) -> None:
        pf = PhenotypicFeature(
            type=OntologyClass(id="HP:0001250", label="Seizure"),
        )
        assert pf.type.id == "HP:0001250"
        assert pf.excluded is False

    def test_excluded(self) -> None:
        pf = PhenotypicFeature(
            type=OntologyClass(id="HP:0001250"),
            excluded=True,
        )
        assert pf.excluded is True

    def test_with_evidence(self) -> None:
        pf = PhenotypicFeature(
            type=OntologyClass(id="HP:0001250"),
            evidence=[
                Evidence(
                    evidence_code=OntologyClass(id="ECO:0000033"),
                    reference=ExternalReference(id="PMID:123"),
                ),
            ],
        )
        assert pf.evidence is not None
        assert len(pf.evidence) == 1


class TestDisease:
    def test_basic(self) -> None:
        d = Disease(term=OntologyClass(id="OMIM:154700", label="Marfan syndrome"))
        assert d.term.id == "OMIM:154700"
        assert d.excluded is False

    def test_with_onset(self) -> None:
        d = Disease(
            term=OntologyClass(id="OMIM:154700"),
            onset=TimeElement(age={"iso8601duration": "P10Y"}),
        )
        assert d.onset is not None


class TestIndividual:
    def test_minimal(self) -> None:
        ind = Individual(id="patient-1")
        assert ind.id == "patient-1"
        assert ind.sex is None

    def test_full(self) -> None:
        ind = Individual(
            id="patient-1",
            sex=Sex.FEMALE,
            karyotypic_sex=KaryotypicSex.XX,
            vital_status=VitalStatus.ALIVE,
            date_of_birth="1990-01-01",
        )
        assert ind.sex == Sex.FEMALE
        assert ind.karyotypic_sex == KaryotypicSex.XX


class TestBiosample:
    def test_minimal(self) -> None:
        bs = Biosample(id="sample-1")
        assert bs.id == "sample-1"
        assert bs.individual_id is None

    def test_with_tissue(self) -> None:
        bs = Biosample(
            id="sample-1",
            individual_id="patient-1",
            sampled_tissue=OntologyClass(id="UBERON:0000955", label="brain"),
        )
        assert bs.sampled_tissue is not None


class TestProcedure:
    def test_basic(self) -> None:
        p = Procedure(code=OntologyClass(id="NCIT:C51580", label="Biopsy"))
        assert p.code.id == "NCIT:C51580"


class TestInterpretation:
    def test_minimal(self) -> None:
        interp = Interpretation(id="interp-1")
        assert interp.progress_status == ProgressStatus.UNKNOWN_PROGRESS

    def test_with_diagnosis(self) -> None:
        interp = Interpretation(
            id="interp-1",
            progress_status=ProgressStatus.SOLVED,
            diagnosis=Diagnosis(
                disease=OntologyClass(id="OMIM:154700"),
                genomic_interpretations=[
                    GenomicInterpretation(
                        subject_or_biosample_id="patient-1",
                        interpretation_status=ProgressStatus.COMPLETED,
                        variant_interpretation=VariantInterpretation(
                            acmg_pathogenicity_classification=AcmgPathogenicityClassification.PATHOGENIC,
                            therapeutic_actionability=TherapeuticActionability.ACTIONABLE,
                        ),
                    ),
                ],
            ),
        )
        assert interp.progress_status == ProgressStatus.SOLVED
        assert interp.diagnosis is not None
        assert interp.diagnosis.genomic_interpretations is not None


class TestMeasurement:
    def test_basic(self) -> None:
        m = Measurement(assay=OntologyClass(id="LOINC:2339-0", label="Glucose"))
        assert m.assay.id == "LOINC:2339-0"


class TestMedicalAction:
    def test_procedure(self) -> None:
        ma = MedicalAction(
            procedure=Procedure(code=OntologyClass(id="NCIT:C51580")),
        )
        assert ma.procedure is not None


class TestMetaData:
    def test_basic(self) -> None:
        md = MetaData(
            created="2024-01-01T00:00:00Z",
            created_by="ai4gh",
            phenopacket_schema_version="2.0.0",
        )
        assert md.phenopacket_schema_version == "2.0.0"


class TestPhenopacket:
    def test_minimal(self) -> None:
        pp = Phenopacket(id="pp-1")
        assert pp.id == "pp-1"
        assert pp.subject is None
        assert pp.phenotypic_features is None

    def test_full(self) -> None:
        pp = Phenopacket(
            id="pp-1",
            subject=Individual(id="patient-1", sex=Sex.MALE),
            phenotypic_features=[
                PhenotypicFeature(
                    type=OntologyClass(id="HP:0001250", label="Seizure"),
                ),
            ],
            diseases=[
                Disease(term=OntologyClass(id="OMIM:154700", label="Marfan syndrome")),
            ],
            interpretations=[
                Interpretation(id="interp-1"),
            ],
            meta_data=MetaData(
                phenopacket_schema_version="2.0.0",
            ),
        )
        assert pp.subject is not None
        assert pp.subject.sex == Sex.MALE
        assert pp.phenotypic_features is not None
        assert len(pp.phenotypic_features) == 1
        assert pp.diseases is not None
        assert pp.meta_data is not None


class TestFamily:
    def test_basic(self) -> None:
        proband = Phenopacket(
            id="pp-proband",
            subject=Individual(id="patient-1"),
        )
        family = Family(
            id="family-1",
            proband=proband,
            relatives=[
                Phenopacket(id="pp-mother", subject=Individual(id="patient-2")),
            ],
        )
        assert family.id == "family-1"
        assert family.proband.id == "pp-proband"
        assert family.relatives is not None
        assert len(family.relatives) == 1


class TestCohort:
    def test_basic(self) -> None:
        cohort = Cohort(
            id="cohort-1",
            description="Test cohort",
            members=[
                Phenopacket(id="pp-1"),
                Phenopacket(id="pp-2"),
            ],
        )
        assert cohort.id == "cohort-1"
        assert cohort.members is not None
        assert len(cohort.members) == 2
