"""Tests for HallucinationCheck."""

from __future__ import annotations

from unittest.mock import MagicMock, patch

import pytest

from ai_qa.checks.hallucination import HallucinationCheck
from ai_qa.core.context import Context


def test_hallucination_check_name():
    """Test HallucinationCheck has correct name."""
    check = HallucinationCheck()
    assert check.name == "hallucination"


def test_hallucination_check_requires_llm():
    """Test HallucinationCheck requires LLM."""
    check = HallucinationCheck()
    assert check.requires_llm is True


def test_hallucination_check_version():
    """Test HallucinationCheck has correct version."""
    check = HallucinationCheck()
    assert check.version == "1.0.0"


@patch("ai_qa.checks.hallucination.get_provider")
def test_hallucination_check_no_hallucinations(mock_get_provider):
    """Test HallucinationCheck when output has no hallucinations."""
    mock_provider = MagicMock()
    mock_provider.judge.return_value = {
        "score": 1.0,
        "evidence": "No hallucinations detected",
    }
    mock_get_provider.return_value = mock_provider

    check = HallucinationCheck()
    ctx = Context(
        input="What is gravity?",
        output="Gravity is a force that attracts objects with mass.",
        context=["Gravity is a fundamental force"],
    )
    result = check.evaluate(ctx)

    assert result.passed is True
    assert result.score == 1.0
    assert "no hallucinations" in result.evidence.lower()


@patch("ai_qa.checks.hallucination.get_provider")
def test_hallucination_check_with_hallucinations(mock_get_provider):
    """Test HallucinationCheck when output contains hallucinations."""
    mock_provider = MagicMock()
    mock_provider.judge.return_value = {
        "score": 0.2,
        "evidence": "Hallucination detected: Claims Einstein invented gravity",
    }
    mock_get_provider.return_value = mock_provider

    check = HallucinationCheck()
    ctx = Context(
        input="What is gravity?",
        output="Einstein invented gravity in 1905.",
        context=["Gravity is a fundamental force"],
    )
    result = check.evaluate(ctx)

    assert result.passed is False
    assert result.score == 0.2
    assert "hallucination" in result.evidence.lower()


@patch("ai_qa.checks.hallucination.get_provider")
def test_hallucination_check_with_cost_tracking(mock_get_provider):
    """Test HallucinationCheck includes cost and token tracking."""
    mock_provider = MagicMock()
    mock_provider.judge.return_value = {
        "score": 0.9,
        "evidence": "Mostly accurate",
        "cost_usd": 0.001,
        "tokens": {"input": 50, "output": 30},
    }
    mock_get_provider.return_value = mock_provider

    check = HallucinationCheck()
    ctx = Context(output="The answer is correct.")
    result = check.evaluate(ctx)

    assert result.cost_usd == 0.001
    assert result.tokens_used == {"input": 50, "output": 30}


@patch("ai_qa.checks.hallucination.get_provider")
def test_hallucination_check_custom_threshold(mock_get_provider):
    """Test HallucinationCheck with custom threshold."""
    mock_provider = MagicMock()
    mock_provider.judge.return_value = {"score": 0.7, "evidence": "Some issues"}
    mock_get_provider.return_value = mock_provider

    check = HallucinationCheck(threshold=0.8)
    ctx = Context(output="Some output")
    result = check.evaluate(ctx)

    assert result.passed is False
    assert result.threshold == 0.8


@patch("ai_qa.checks.hallucination.get_provider")
def test_hallucination_check_without_context(mock_get_provider):
    """Test HallucinationCheck without context provided."""
    mock_provider = MagicMock()
    mock_provider.judge.return_value = {"score": 0.9, "evidence": "OK"}
    mock_get_provider.return_value = mock_provider

    check = HallucinationCheck()
    ctx = Context(input="What is X?", output="X is Y")
    result = check.evaluate(ctx)

    assert result.passed is True


@patch("ai_qa.checks.hallucination.get_provider")
def test_hallucination_check_provider_called_correctly(mock_get_provider):
    """Test HallucinationCheck calls provider.judge() with correct arguments."""
    mock_provider = MagicMock()
    mock_provider.judge.return_value = {"score": 0.5, "evidence": "Test result"}
    mock_get_provider.return_value = mock_provider

    check = HallucinationCheck()
    ctx = Context(
        input="Question",
        output="Answer",
        context=["Context 1", "Context 2"],
    )
    check.evaluate(ctx)

    # Verify provider.judge was called
    assert mock_provider.judge.called
    call_args = mock_provider.judge.call_args
    assert "system" in call_args.kwargs or len(call_args.args) > 0
    assert "user" in call_args.kwargs or len(call_args.args) > 1


def test_hallucination_check_default_score():
    """Test HallucinationCheck default score when not provided by provider."""
    with patch("ai_qa.checks.hallucination.get_provider") as mock_get_provider:
        mock_provider = MagicMock()
        mock_provider.judge.return_value = {"evidence": "No score"}
        mock_get_provider.return_value = mock_provider

        check = HallucinationCheck()
        ctx = Context(output="Test")
        result = check.evaluate(ctx)

        assert result.score == 0.5  # Default score
