import json
from typing import List, Union, cast

import pytest
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, ToolMessage

import ragas.messages as r
from ragas.integrations.langgraph import convert_to_ragas_messages


def test_human_message_conversion():
    """Test conversion of HumanMessage with valid string content"""
    messages = [
        HumanMessage(content="Hello, add 4 and 5"),
        ToolMessage(content="9", tool_call_id="1"),
    ]
    result = convert_to_ragas_messages(messages)

    assert len(result) == 2
    assert isinstance(result[0], r.HumanMessage)
    assert result[0].content == "Hello, add 4 and 5"


def test_human_message_invalid_content():
    """Test HumanMessage with invalid content type raises TypeError"""
    messages: List[Union[HumanMessage, SystemMessage, AIMessage, ToolMessage]] = [
        HumanMessage(content=["invalid", "content"])
    ]

    with pytest.raises(TypeError) as exc_info:
        convert_to_ragas_messages(messages)
    assert "HumanMessage content must be a string" in str(exc_info.value)


def test_ai_message_conversion():
    """Test conversion of AIMessage with valid string content"""
    messages: List[Union[HumanMessage, SystemMessage, AIMessage, ToolMessage]] = [
        AIMessage(content="I'm doing well, thanks!")
    ]
    result = convert_to_ragas_messages(messages)

    assert len(result) == 1
    assert isinstance(result[0], r.AIMessage)
    assert result[0].content == "I'm doing well, thanks!"
    assert result[0].tool_calls is None


def test_ai_message_with_tool_calls():
    """Test conversion of AIMessage with tool calls"""

    tool_calls = [
        {
            "function": {
                "arguments": '{"metal_name": "gold"}',
                "name": "get_metal_price",
            }
        },
        {
            "function": {
                "arguments": '{"metal_name": "silver"}',
                "name": "get_metal_price",
            }
        },
    ]

    messages: List[Union[HumanMessage, SystemMessage, AIMessage, ToolMessage]] = [
        AIMessage(
            content="Find the difference in the price of gold and silver?",
            additional_kwargs={"tool_calls": tool_calls},
        )
    ]

    result = convert_to_ragas_messages(messages)
    assert len(result) == 1
    assert isinstance(result[0], r.AIMessage)
    assert result[0].content == "Find the difference in the price of gold and silver?"
    assert result[0].tool_calls is not None
    assert len(result[0].tool_calls) == 2
    assert result[0].tool_calls[0].name == "get_metal_price"
    assert result[0].tool_calls[0].args == {"metal_name": "gold"}
    assert result[0].tool_calls[1].name == "get_metal_price"
    assert result[0].tool_calls[1].args == {"metal_name": "silver"}


def test_tool_message_conversion():
    """Test conversion of ToolMessage with valid string content"""
    messages = [
        HumanMessage(content="Hello, add 4 and 5"),
        ToolMessage(content="9", tool_call_id="2"),
    ]
    result = convert_to_ragas_messages(messages)

    assert len(result) == 2
    assert isinstance(result[1], r.ToolMessage)
    assert result[1].content == "9"


def test_system_message_skipped():
    """Test that SystemMessages are properly skipped"""
    messages = [SystemMessage(content="System prompt"), HumanMessage(content="Hello")]
    result = convert_to_ragas_messages(messages)

    assert len(result) == 1
    assert isinstance(result[0], r.HumanMessage)
    assert result[0].content == "Hello"


def test_unsupported_message_type():
    """Test that unsupported message types raise ValueError"""

    class CustomMessage:
        content = "test"

    messages = cast(
        List[Union[HumanMessage, SystemMessage, AIMessage, ToolMessage]],
        [CustomMessage()],
    )

    with pytest.raises(ValueError) as exc_info:
        convert_to_ragas_messages(messages)
    assert "Unsupported message type: CustomMessage" in str(exc_info.value)


def test_empty_message_list():
    """Test conversion of empty message list"""
    messages = []
    result = convert_to_ragas_messages(messages)
    assert result == []


def test_invalid_tool_calls_json():
    """Test handling of invalid JSON in tool calls"""
    tool_calls = [{"function": {"name": "search", "arguments": "invalid json"}}]

    messages: List[Union[HumanMessage, SystemMessage, AIMessage, ToolMessage]] = [
        AIMessage(content="Test", additional_kwargs={"tool_calls": tool_calls})
    ]

    with pytest.raises(json.JSONDecodeError):
        convert_to_ragas_messages(messages)
