import pytest
from omibio.sequence import Sequence

# ! Disclaimer: This unit test code was generated by AI
# ! and has been reviewed and modified by humans.


class TestSequence:

    # --------------------------
    # Initialization & property & setter testing
    # --------------------------
    def test_init_dna_rna_strict(self):
        s1 = Sequence("ATGC", rna=False, strict=True)
        assert s1.sequence == "ATGC"
        assert s1.is_rna is False
        assert s1.strict is True
        assert s1.type == "DNA"

        s2 = Sequence("AUGC", rna=True, strict=True)
        assert s2.is_rna is True
        assert s2.type == "RNA"

    def test_init_auto_rna_detection(self):
        # Sequence contains 'U'
        s = Sequence("AUGC", rna=None, strict=True)
        assert s.is_rna is True
        assert s.type == "RNA"
        # Sequence contains 'T'
        s = Sequence("ATGC", rna=None, strict=True)
        assert s.is_rna is False
        assert s.type == "DNA"
        # Sequence contains neither 'U' or 'T'
        s = Sequence("A", rna=None, strict=True)
        assert s.is_rna is False
        assert s.type == "DNA"
        # Empty Sequence
        s = Sequence(rna=None, strict=True)
        assert s.is_rna is False
        assert s.type == "DNA"

    def test_init_invalid_types(self):
        with pytest.raises(TypeError):
            Sequence("ATGC", rna="yes")
        with pytest.raises(TypeError):
            Sequence("ATGC", strict="no")

    # --------------------------
    #  Base checking testing
    # --------------------------
    def test_init_non_strict_invalid_bases_allowed(self):
        s = Sequence("ATBX", rna=False, strict=False)
        assert s.sequence == "ATBX"
        assert s.is_rna is False

        s2 = Sequence("AUGX", rna=None, strict=False)
        assert s2.is_rna is True

    def test_init_strict_invalid_bases(self):
        with pytest.raises(ValueError):
            Sequence("ATGU", rna=None, strict=True)
        with pytest.raises(ValueError):
            Sequence("ATBX", rna=False, strict=True)

    # --------------------------
    # __eq__ testing
    # --------------------------
    def test_eq_strict_same_type(self):
        a = Sequence("ATG", rna=False, strict=True)
        b = Sequence("ATG", rna=False, strict=True)
        assert a == b

    def test_eq_strict_different_type(self):
        a = Sequence("AG", rna=True, strict=True)
        b = Sequence("AG", rna=False, strict=True)
        assert not (a == b)

    def test_eq_non_strict_ignores_type(self):
        a = Sequence("ATG", rna=False, strict=False)
        b = Sequence("ATG", rna=True, strict=False)
        assert a == b

    def test_eq_with_string(self):
        s = Sequence("ATG", rna=False, strict=True)
        assert s == "ATG"
        assert s == "atg"
        assert not (s == "AUG")

    def test_eq_with_other_types(self):
        s = Sequence("ATG", rna=False, strict=True)
        assert not (s == [1, 2])
        assert not (s == {1, 2})
        assert not (s == {"a": 1})
        assert not (s == (1, 2))

    # --------------------------
    # __add__ testing
    # --------------------------
    def test_add_same_type_strict(self):
        a = Sequence("AT", rna=False, strict=True)
        b = Sequence("GC", rna=False, strict=True)
        c = a + b
        assert isinstance(c, Sequence)
        assert c.sequence == "ATGC"
        assert c.is_rna is False
        assert c.strict is True

    def test_add_different_type_strict_error(self):
        a = Sequence("AT", rna=False, strict=True)
        b = Sequence("UG", rna=True, strict=True)
        with pytest.raises(TypeError):
            _ = a + b

    def test_add_same_type_non_strict(self):
        a = Sequence("AT", rna=False, strict=False)
        b = Sequence("GC", rna=True, strict=False)
        c = a + b
        assert c.sequence == "ATGC"
        assert c.is_rna is False

    def test_add_with_str(self):
        a = Sequence("ATG", rna=False, strict=True)
        b = "C"
        c = a + b
        assert c.sequence == "ATGC"
        assert c.is_rna is False

    def test_add_strict_str_conflict_error(self):
        a = Sequence("ATG", rna=False, strict=True)
        b = "U"
        with pytest.raises(TypeError):
            _ = a + b

    def test_add_invalid_type(self):
        s = Sequence("AT")
        with pytest.raises(TypeError):
            _ = s + 123
        with pytest.raises(TypeError):
            _ = s + [1, 2]

    def test_add_invalid_str(self):
        s = Sequence("A", strict=True)
        with pytest.raises(ValueError):
            _ = s + "TU"

    # --------------------------
    # complement & reverse_complement testing
    # --------------------------
    def test_complement_dna(self):
        s = Sequence("ATGC", rna=False)
        comp = s.complement()
        assert comp.sequence == "TACG"
        assert comp.is_rna is False

    def test_complement_rna(self):
        s = Sequence("AUGC", rna=True)
        comp = s.complement()
        assert comp.sequence == "UACG"
        assert comp.is_rna is True

    def test_reverse_complement_dna(self):
        s = Sequence("ATGC", rna=False)
        rev = s.reverse_complement()
        assert rev.sequence == "GCAT"
        assert rev.is_rna is False

    def test_complement_with_iupac_bases(self):
        s = Sequence("RYKMBVDH", rna=False, strict=False)
        comp = s.complement()
        assert comp.sequence == "YRMKVBHD"

    # --------------------------
    # transcribe & reverse_transcribe testing
    # --------------------------
    def test_transcribe_sense_strand(self):
        s = Sequence("ATGC", rna=False)
        rna = s.transcribe(strand="+")
        assert rna.sequence == "AUGC"
        assert rna.is_rna is True

    def test_transcribe_antisense_strand(self):
        s = Sequence("ATGC", rna=False)
        rna = s.transcribe(strand="-")
        # 补链后转U
        assert rna.sequence == "GCAU"
        assert rna.is_rna is True

    def test_reverse_transcribe(self):
        s = Sequence("AUGC", rna=True)
        dna = s.reverse_transcribe()
        assert dna.sequence == "ATGC"
        assert dna.is_rna is False

    def test_transcribe_idempotent_for_rna(self):
        rna = Sequence("AUGC", rna=True)
        assert rna.transcribe() == rna

    def test_reverse_transcribe_idempotent_for_dna(self):
        dna = Sequence("ATGC", rna=False)
        assert dna.reverse_transcribe() == dna

    def test_invalid_strand_type(self):
        s = Sequence("ATCG")
        with pytest.raises(ValueError):
            s.transcribe(strand=1)
        with pytest.raises(ValueError):
            s.transcribe(strand='sense')

    # --------------------------
    # subseq testing
    # --------------------------
    def test_subseq_basic(self):
        s = Sequence("ATGCGT", rna=False)
        sub = s.subseq(1, 4)
        assert sub.sequence == "TGC"
        assert sub.is_rna is False

    def test_subseq_type_error(self):
        s = Sequence("ATGC", rna=False)
        with pytest.raises(TypeError):
            s.subseq("0", 2)
        with pytest.raises(TypeError):
            s.subseq(0, "2")

    def test_subseq_edge_cases(self):
        s = Sequence("ATGC")
        assert s.subseq(0, 10).sequence == "ATGC"
        assert s.subseq(-2).sequence == "GC"
        assert s.subseq(2, 2).sequence == ""
        assert s.subseq(0, None).sequence == "ATGC"

    # --------------------------
    # Empty Sequence testing
    # --------------------------
    def test_operations_on_empty_sequence(self):
        s = Sequence("")
        assert s.gc_content() == 0.0
        assert s.gc_content(percent=True) == "0.00%"
        assert len(s) == 0
        assert s.complement().sequence == ""
        assert s.reverse_complement().sequence == ""
        assert s.transcribe().sequence == ""
        assert s.reverse_transcribe().sequence == ""
        assert s.subseq(0).sequence == ""

    # --------------------------
    # gc_content testing
    # --------------------------
    def test_gc_content_float_and_percent(self):
        s = Sequence("ATGC", rna=False)
        assert s.gc_content() == 0.5
        assert s.gc_content(percent=True) == "50.00%"

    # --------------------------
    # __mul__ testing
    # --------------------------
    def test_mul_positive(self):
        s = Sequence("AT")
        t = s * 3
        assert t.sequence == "ATATAT"

    def test_mul_invalid(self):
        s = Sequence("AT")
        with pytest.raises(TypeError):
            _ = s * "2"
        with pytest.raises(ValueError):
            _ = s * -1

    # --------------------------
    # Other magic methods testing
    # --------------------------
    def test_magic_methods(self):
        s = Sequence("ATGC", rna=False, strict=True)
        assert "T" in s
        assert "X" not in s
        assert list(s) == ["A", "T", "G", "C"]
        assert s[0] == "A"
        assert s[-1] == "C"
        assert repr(s) == "Sequence('ATGC', type=DNA, strict=True)"
        assert str(s) == "ATGC"
