from __future__ import annotations

from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from leshy.core.models import TargetConfig
from leshy.targets.langgraph import LangGraphTarget


class TestLangGraphTarget:
    @pytest.mark.asyncio
    async def test_send_basic(self) -> None:
        mock_client = MagicMock()
        mock_client.assistants = MagicMock()
        mock_client.assistants.search = AsyncMock(
            return_value=[{"assistant_id": "test-assistant"}]
        )
        mock_client.threads = MagicMock()
        mock_client.threads.create = AsyncMock(return_value={"thread_id": "test-thread"})

        async def mock_stream(*args, **kwargs):
            chunk = MagicMock()
            chunk.data = {"messages": [{"role": "ai", "content": "Hello, how can I help?"}]}
            yield chunk

        mock_client.runs = MagicMock()
        mock_client.runs.stream = mock_stream

        config = TargetConfig(type="langgraph", url="http://localhost:8123")
        target = LangGraphTarget(config)

        with patch.object(target, "_get_client", return_value=mock_client):
            await target.setup()
            result = await target.send("test payload")

        assert result.content == "Hello, how can I help?"
        assert result.thread_id == "test-thread"
        assert result.latency_ms > 0

    @pytest.mark.asyncio
    async def test_send_with_existing_thread(self) -> None:
        mock_client = MagicMock()
        mock_client.assistants = MagicMock()
        mock_client.assistants.search = AsyncMock(
            return_value=[{"assistant_id": "test-assistant"}]
        )

        async def mock_stream(*args, **kwargs):
            chunk = MagicMock()
            chunk.data = {"messages": [{"content": "Response"}]}
            yield chunk

        mock_client.runs = MagicMock()
        mock_client.runs.stream = mock_stream

        config = TargetConfig(type="langgraph", url="http://localhost:8123")
        target = LangGraphTarget(config)

        with patch.object(target, "_get_client", return_value=mock_client):
            await target.setup()
            result = await target.send("test", thread_id="existing-thread")

        assert result.thread_id == "existing-thread"

    @pytest.mark.asyncio
    async def test_assistant_id_from_header(self) -> None:
        mock_client = MagicMock()
        mock_client.threads = MagicMock()
        mock_client.threads.create = AsyncMock(return_value={"thread_id": "t1"})

        async def mock_stream(*args, **kwargs):
            chunk = MagicMock()
            chunk.data = {"messages": [{"content": "ok"}]}
            yield chunk

        mock_client.runs = MagicMock()
        mock_client.runs.stream = mock_stream

        config = TargetConfig(
            type="langgraph",
            url="http://localhost:8123",
            headers={"x-assistant-id": "my-custom-assistant"},
        )
        target = LangGraphTarget(config)

        with patch.object(target, "_get_client", return_value=mock_client):
            await target.setup()
            result = await target.send("test")

        assert result.content == "ok"

    @pytest.mark.asyncio
    async def test_error_handled(self) -> None:
        mock_client = MagicMock()
        mock_client.assistants = MagicMock()
        mock_client.assistants.search = AsyncMock(
            return_value=[{"assistant_id": "test-assistant"}]
        )
        mock_client.threads = MagicMock()
        mock_client.threads.create = AsyncMock(side_effect=Exception("Connection failed"))

        config = TargetConfig(type="langgraph", url="http://localhost:8123")
        target = LangGraphTarget(config)

        with patch.object(target, "_get_client", return_value=mock_client):
            await target.setup()
            result = await target.send("test")

        assert result.status_code == 0
        assert "Error" in result.content

    @pytest.mark.asyncio
    async def test_no_assistants_raises(self) -> None:
        mock_client = MagicMock()
        mock_client.assistants = MagicMock()
        mock_client.assistants.search = AsyncMock(return_value=[])

        config = TargetConfig(type="langgraph", url="http://localhost:8123")
        target = LangGraphTarget(config)

        with patch.object(target, "_get_client", return_value=mock_client):
            with pytest.raises(RuntimeError, match="No assistants found"):
                await target.setup()
