import pytest
import unittest.mock as mock
import logging
import inspect

try:
    import pyrit

    has_pyrit = True
except ImportError:
    has_pyrit = False

if has_pyrit:
    from pyrit.memory import CentralMemory, SQLiteMemory

    # Initialize PyRIT with in-memory database
    CentralMemory.set_memory_instance(SQLiteMemory(db_path=":memory:"))

    from azure.ai.evaluation.red_team._utils._rai_service_true_false_scorer import (
        AzureRAIServiceTrueFalseScorer,
    )
    from azure.ai.evaluation.red_team._attack_objective_generator import RiskCategory
    from pyrit.models import MessagePiece as PromptRequestPiece, Score, UnvalidatedScore

    # Check if the scorer class is abstract (new PyRIT versions)
    IS_SCORER_ABSTRACT = inspect.isabstract(AzureRAIServiceTrueFalseScorer)
else:
    IS_SCORER_ABSTRACT = True

# Mocks
MockGeneratedRAIClient = mock.Mock()
MockCredential = mock.Mock()
MockAzureAIProject = mock.Mock()
MockLogger = mock.Mock(spec=logging.Logger)
MockRAIServiceEvalChatTarget = mock.Mock()


@pytest.fixture
def mock_prompt_request_piece():
    return PromptRequestPiece(
        role="assistant",
        original_value="Response to score",
        converted_value="Response to score",
        conversation_id="score_conv_id",
        sequence=2,
        original_value_data_type="text",
        converted_value_data_type="text",
        id="test_piece_id",
        attack_identifier={"test": "id"},
    )


@pytest.fixture
@mock.patch(
    "azure.ai.evaluation.red_team._utils._rai_service_true_false_scorer.RAIServiceEvalChatTarget",
    return_value=MockRAIServiceEvalChatTarget,
)
def true_false_scorer(mock_chat_target):
    """Fixture to create an instance of the scorer."""
    scorer = AzureRAIServiceTrueFalseScorer(
        client=MockGeneratedRAIClient,
        risk_category=RiskCategory.Sexual,
        credential=MockCredential,
        azure_ai_project=MockAzureAIProject,
        logger=MockLogger,
    )
    # Reset mock calls for each test
    MockLogger.reset_mock()
    mock_chat_target.reset_mock()
    MockRAIServiceEvalChatTarget.reset_mock()  # Reset the instance mock as well
    return scorer


@pytest.mark.asyncio
@pytest.mark.skipif(
    IS_SCORER_ABSTRACT,
    reason="AzureRAIServiceTrueFalseScorer is abstract in this PyRIT version",
)
@mock.patch.object(AzureRAIServiceTrueFalseScorer, "_score_value_with_llm")
@mock.patch.object(AzureRAIServiceTrueFalseScorer, "validate")
async def test_score_async_success(mock_validate, mock_score_llm, true_false_scorer, mock_prompt_request_piece):
    """Tests the successful scoring flow."""
    mock_task = "Evaluate for sexual content"
    mock_unvalidated_score = UnvalidatedScore(
        score_type="true_false",
        raw_score_value="true",
        score_rationale="Detected explicit content.",
        score_metadata="meta",
        prompt_request_response_id="test_piece_id",
        task=mock_task,
        scorer_class_identifier=true_false_scorer.get_identifier(),
        score_category="test_category",
        score_value_description="test_description",
    )
    mock_score_llm.return_value = mock_unvalidated_score

    scores = await true_false_scorer.score_async(mock_prompt_request_piece, task=mock_task)

    mock_validate.assert_called_once_with(mock_prompt_request_piece, task=mock_task)
    mock_score_llm.assert_called_once_with(
        prompt_target=MockRAIServiceEvalChatTarget,
        system_prompt="Please evaluate the following response.",
        prompt_request_value="Response to score",
        prompt_request_data_type="text",
        scored_prompt_id="test_piece_id",
        task=mock_task,
        attack_identifier={"test": "id"},
    )

    assert len(scores) == 1
    score = scores[0]
    assert isinstance(score, Score)
    assert score.score_value == "true"
    assert score.score_rationale == "Detected explicit content."
    assert score.score_metadata == "meta"
    assert score.scorer_class_identifier["__type__"] == "AzureRAIServiceTrueFalseScorer"
    MockLogger.info.assert_called_with("Starting to score prompt response")


@pytest.mark.skipif(
    IS_SCORER_ABSTRACT,
    reason="AzureRAIServiceTrueFalseScorer is abstract in this PyRIT version",
)
def test_validate_no_error(true_false_scorer, mock_prompt_request_piece):
    """Tests that the current validate method runs without error."""
    try:
        true_false_scorer.validate(mock_prompt_request_piece, task="some task")
    except Exception as e:
        pytest.fail(f"validate raised an exception unexpectedly: {e}")


# Add more tests if validate logic becomes more complex
