"""tests/test_scoring_direct_generation_guard.py — Unit tests for llm_guard.scoring.direct_generation_guard."""
import pytest
import math
from unittest.mock import MagicMock, patch

pytestmark = pytest.mark.unit


class TestImport:
    def test_import_guard(self):
        from llm_guard.scoring.direct_generation_guard import DirectGenerationGuard
        assert DirectGenerationGuard is not None

    def test_import_result(self):
        from llm_guard.scoring.direct_generation_guard import DirectGenResult
        assert DirectGenResult is not None


class TestInstantiation:
    def test_default_construction(self):
        from llm_guard.scoring.direct_generation_guard import DirectGenerationGuard
        guard = DirectGenerationGuard()
        assert guard is not None

    def test_no_api_key(self):
        from llm_guard.scoring.direct_generation_guard import DirectGenerationGuard
        guard = DirectGenerationGuard(api_key="")
        assert guard._api_key == ""


class TestLexicalSignals:
    def test_answer_length_norm(self):
        from llm_guard.scoring.direct_generation_guard import DirectGenerationGuard
        guard = DirectGenerationGuard()
        short = guard._answer_length_norm("hi")
        long = guard._answer_length_norm(" ".join(["word"] * 60))
        assert short < long
        assert long == 1.0

    def test_hedging_density_no_hedges(self):
        from llm_guard.scoring.direct_generation_guard import DirectGenerationGuard
        guard = DirectGenerationGuard()
        d = guard._hedging_density("Paris is the capital of France.")
        assert d == 0.0

    def test_hedging_density_with_hedges(self):
        from llm_guard.scoring.direct_generation_guard import DirectGenerationGuard
        guard = DirectGenerationGuard()
        d = guard._hedging_density("I think this might possibly be correct.")
        assert d > 0.0

    def test_qa_lexical_overlap(self):
        from llm_guard.scoring.direct_generation_guard import DirectGenerationGuard
        guard = DirectGenerationGuard()
        overlap = guard._qa_lexical_overlap("What is Paris?", "Paris is a city.")
        assert overlap > 0.0

    def test_answer_completeness_with_punct(self):
        from llm_guard.scoring.direct_generation_guard import DirectGenerationGuard
        guard = DirectGenerationGuard()
        c = guard._answer_completeness("Paris is a city.")
        assert c == 1.0

    def test_answer_completeness_no_punct(self):
        from llm_guard.scoring.direct_generation_guard import DirectGenerationGuard
        guard = DirectGenerationGuard()
        c = guard._answer_completeness("Paris")
        assert c == 0.5


class TestScoreWithoutPtrue:
    def test_score_lexical_only(self):
        from llm_guard.scoring.direct_generation_guard import DirectGenerationGuard
        guard = DirectGenerationGuard(api_key="")
        result = guard.score("What is Paris?", "Paris is the capital of France.", use_ptrue=False)
        assert 0.0 <= result.risk_score <= 1.0
        assert math.isnan(result.ptrue_score)

    def test_result_has_signals(self):
        from llm_guard.scoring.direct_generation_guard import DirectGenerationGuard
        guard = DirectGenerationGuard(api_key="")
        result = guard.score("What is Paris?", "Paris is the capital.", use_ptrue=False)
        assert "answer_length_norm" in result.signals
        assert "hedging_density" in result.signals

    def test_confidence_tier_valid(self):
        from llm_guard.scoring.direct_generation_guard import DirectGenerationGuard
        guard = DirectGenerationGuard(api_key="")
        result = guard.score("Q", "Short answer.", use_ptrue=False)
        assert result.confidence_tier in ("HIGH", "MEDIUM", "LOW")

    def test_no_api_key_no_ptrue(self):
        from llm_guard.scoring.direct_generation_guard import DirectGenerationGuard
        guard = DirectGenerationGuard(api_key="")
        result = guard.score("Q", "A", use_ptrue=True)  # key absent → no ptrue
        assert math.isnan(result.ptrue_score)


class TestScoreWithPtrue:
    @patch("llm_guard.scoring.direct_generation_guard.DirectGenerationGuard._call_ptrue")
    def test_ptrue_called_when_key_set(self, mock_ptrue):
        from llm_guard.scoring.direct_generation_guard import DirectGenerationGuard
        mock_ptrue.return_value = 0.8
        guard = DirectGenerationGuard(api_key="sk-ant-fake")
        result = guard.score("What is Paris?", "Paris.", use_ptrue=True)
        mock_ptrue.assert_called_once()
        assert not math.isnan(result.ptrue_score)
        assert result.ptrue_score == 0.8


class TestJaccard:
    def test_identical_strings(self):
        from llm_guard.scoring.direct_generation_guard import DirectGenerationGuard
        guard = DirectGenerationGuard()
        assert guard._jaccard("hello world", "hello world") == 1.0

    def test_empty_strings(self):
        from llm_guard.scoring.direct_generation_guard import DirectGenerationGuard
        guard = DirectGenerationGuard()
        assert guard._jaccard("", "") == 1.0

    def test_no_overlap(self):
        from llm_guard.scoring.direct_generation_guard import DirectGenerationGuard
        guard = DirectGenerationGuard()
        assert guard._jaccard("apple banana", "cat dog") == 0.0
