"""Tests for HeuristicAMPScorer."""
import pytest

from peptidegym.peptide.amp_scoring import HeuristicAMPScorer


@pytest.fixture
def scorer():
    return HeuristicAMPScorer()


def test_scorer_returns_dict(scorer):
    """score() returns a dictionary."""
    result = scorer.score("KRLKRL")
    assert isinstance(result, dict)


def test_scorer_keys(scorer):
    """Result dict contains the four expected keys."""
    result = scorer.score("KRLKRL")
    expected_keys = {"activity_score", "toxicity_penalty", "amphipathicity_bonus", "length_fitness"}
    assert set(result.keys()) == expected_keys


def test_activity_score_cationic_peptide(scorer):
    """Cationic amphipathic peptide (KRLKRL) should score higher than polyglycine."""
    cationic = scorer.score("KRLKRL")
    inert = scorer.score("GGGGGG")
    assert cationic["activity_score"] > inert["activity_score"]


def test_activity_score_range(scorer):
    """activity_score should be in [0, 1]."""
    for seq in ["KRLKRL", "GGGGGG", "A", "WWWWWWWWWWWWWWWWWWWW"]:
        result = scorer.score(seq)
        assert 0.0 <= result["activity_score"] <= 1.0


def test_toxicity_low_for_normal(scorer):
    """Normal sequence should have low toxicity penalty."""
    result = scorer.score("KRLKRLAGIV")
    assert result["toxicity_penalty"] < 0.5


def test_toxicity_high_for_extreme(scorer):
    """All-tryptophan sequence should have higher toxicity than normal."""
    extreme = scorer.score("WWWWWW")
    normal = scorer.score("KRLKRL")
    assert extreme["toxicity_penalty"] >= normal["toxicity_penalty"]


def test_amphipathicity_nonzero(scorer):
    """A mixed sequence should have nonzero amphipathicity bonus."""
    result = scorer.score("KLAKLAKLAK")
    assert result["amphipathicity_bonus"] > 0.0


def test_length_fitness_optimal(scorer):
    """20-mer should score near 1.0 for length fitness (Gaussian centered at 20)."""
    result = scorer.score("A" * 20)
    assert result["length_fitness"] > 0.9


def test_length_fitness_too_short(scorer):
    """3-mer should score lower than optimal for length fitness."""
    short = scorer.score("AAA")
    optimal = scorer.score("A" * 20)
    assert short["length_fitness"] < optimal["length_fitness"]


def test_length_fitness_too_long(scorer):
    """60-mer should score lower than optimal."""
    long_seq = scorer.score("A" * 60)
    optimal = scorer.score("A" * 20)
    assert long_seq["length_fitness"] < optimal["length_fitness"]


def test_scorer_empty_sequence_handled(scorer):
    """Empty string should not crash; mean_hydrophobicity returns 0 for empty."""
    # Empty sequence will cause division issues in some sub-functions
    # but the scorer should handle gracefully
    result = scorer.score("")
    assert isinstance(result, dict)
    assert len(result) == 4


def test_scorer_single_residue(scorer):
    """Single residue should return valid scores."""
    result = scorer.score("K")
    assert isinstance(result, dict)
    for v in result.values():
        assert isinstance(v, float)
