"""Tests for VRS v2 models."""

from __future__ import annotations

from ai4gh.specs.vrs.models import (
    Adjacency,
    Allele,
    CopyChange,
    CopyNumberChange,
    CopyNumberCount,
    Haplotype,
    LiteralSequenceExpression,
    ReferenceLengthExpression,
    SequenceLocation,
    SequenceReference,
    SequenceString,
    ga4gh_digest,
    ga4gh_identify,
)


class TestSequenceString:
    def test_basic(self) -> None:
        ss = SequenceString(sequence="ACGT")
        assert ss.sequence == "ACGT"
        assert ss.type == "SequenceString"


class TestSequenceReference:
    def test_basic(self) -> None:
        sr = SequenceReference(refgetAccession="SQ.abc123")
        assert sr.refgetAccession == "SQ.abc123"
        assert sr.type == "SequenceReference"

    def test_with_alphabet(self) -> None:
        sr = SequenceReference(
            refgetAccession="SQ.abc123",
            residueAlphabet="aa",
        )
        assert sr.residueAlphabet == "aa"


class TestSequenceLocation:
    def test_basic(self) -> None:
        loc = SequenceLocation(
            sequenceReference=SequenceReference(refgetAccession="SQ.abc"),
            start=100,
            end=200,
        )
        assert loc.start == 100
        assert loc.end == 200
        assert loc.type == "SequenceLocation"

    def test_with_range_coords(self) -> None:
        loc = SequenceLocation(
            sequenceReference=SequenceReference(refgetAccession="SQ.abc"),
            start=[10, 20],
            end=[30, 40],
        )
        assert loc.start == [10, 20]


class TestLiteralSequenceExpression:
    def test_basic(self) -> None:
        expr = LiteralSequenceExpression(sequence="T")
        assert expr.sequence == "T"
        assert expr.type == "LiteralSequenceExpression"


class TestReferenceLengthExpression:
    def test_basic(self) -> None:
        expr = ReferenceLengthExpression(length=5)
        assert expr.length == 5
        assert expr.type == "ReferenceLengthExpression"


class TestAllele:
    def test_basic(self) -> None:
        allele = Allele(
            location=SequenceLocation(
                sequenceReference=SequenceReference(refgetAccession="SQ.abc"),
                start=100,
                end=101,
            ),
            state=LiteralSequenceExpression(sequence="T"),
        )
        assert allele.type == "Allele"
        assert allele.location.start == 100
        assert allele.id is None

    def test_with_id(self) -> None:
        allele = Allele(
            id="ga4gh:VA.abc123",
            location=SequenceLocation(start=0, end=1),
            state=LiteralSequenceExpression(sequence="A"),
        )
        assert allele.id == "ga4gh:VA.abc123"


class TestHaplotype:
    def test_basic(self) -> None:
        allele1 = Allele(
            location=SequenceLocation(start=100, end=101),
            state=LiteralSequenceExpression(sequence="T"),
        )
        allele2 = Allele(
            location=SequenceLocation(start=200, end=201),
            state=LiteralSequenceExpression(sequence="G"),
        )
        hap = Haplotype(members=[allele1, allele2])
        assert hap.type == "Haplotype"
        assert len(hap.members) == 2


class TestCopyNumberCount:
    def test_basic(self) -> None:
        cnc = CopyNumberCount(
            location=SequenceLocation(
                sequenceReference=SequenceReference(refgetAccession="SQ.abc"),
                start=1000,
                end=2000,
            ),
            copies=3,
        )
        assert cnc.type == "CopyNumberCount"
        assert cnc.copies == 3

    def test_range_copies(self) -> None:
        cnc = CopyNumberCount(
            location=SequenceLocation(start=0, end=100),
            copies={"min": 2, "max": 4},
        )
        assert cnc.copies == {"min": 2, "max": 4}


class TestCopyNumberChange:
    def test_basic(self) -> None:
        cnc = CopyNumberChange(
            location=SequenceLocation(start=0, end=1000),
            copy_change=CopyChange.EFO_0030070,
        )
        assert cnc.type == "CopyNumberChange"
        assert cnc.copy_change == CopyChange.EFO_0030070


class TestAdjacency:
    def test_basic(self) -> None:
        adj = Adjacency(
            adj_left=SequenceLocation(start=100, end=100),
            adj_right=SequenceLocation(start=500, end=500),
        )
        assert adj.type == "Adjacency"
        assert adj.linker_sequence is None

    def test_with_linker(self) -> None:
        adj = Adjacency(
            adj_left=SequenceLocation(start=100, end=100),
            adj_right=SequenceLocation(start=500, end=500),
            linker_sequence="ACGT",
        )
        assert adj.linker_sequence == "ACGT"


class TestCopyChange:
    def test_gain(self) -> None:
        assert CopyChange.EFO_0030070.value == "efo:0030070"

    def test_loss(self) -> None:
        assert CopyChange.EFO_0030068.value == "efo:0030068"


class TestGA4GHDigest:
    def test_deterministic(self) -> None:
        data = {"type": "Allele", "location": {"start": 100}}
        d1 = ga4gh_digest(data)
        d2 = ga4gh_digest(data)
        assert d1 == d2

    def test_different_data(self) -> None:
        d1 = ga4gh_digest({"a": 1})
        d2 = ga4gh_digest({"a": 2})
        assert d1 != d2

    def test_key_order_invariant(self) -> None:
        d1 = ga4gh_digest({"b": 2, "a": 1})
        d2 = ga4gh_digest({"a": 1, "b": 2})
        assert d1 == d2


class TestGA4GHIdentify:
    def test_allele_format(self) -> None:
        data = {"type": "Allele", "location": {"start": 100}}
        identifier = ga4gh_identify(data, "VA")
        assert identifier.startswith("ga4gh:VA.")

    def test_haplotype_format(self) -> None:
        data = {"type": "Haplotype", "members": []}
        identifier = ga4gh_identify(data, "VH")
        assert identifier.startswith("ga4gh:VH.")

    def test_deterministic(self) -> None:
        data = {"type": "Allele", "state": "T"}
        id1 = ga4gh_identify(data, "VA")
        id2 = ga4gh_identify(data, "VA")
        assert id1 == id2
