"""Tests for coda-mcp tools.

These tests mock the HTTP client to test the MCP server logic without
requiring the actual API to be running.
"""

from unittest.mock import patch

import pytest


@pytest.fixture
def mock_post():
    """Mock the HTTP POST function."""
    with patch("coda_mcp.client._post") as mock:
        mock.return_value = {"success": True}
        yield mock


@pytest.fixture
def mock_get():
    """Mock the HTTP GET function."""
    with patch("coda_mcp.client._get") as mock:
        mock.return_value = {"success": True}
        yield mock


class TestQuantumTools:
    """Tests for quantum circuit tools."""

    @pytest.mark.asyncio
    async def test_transpile(self, mock_post):
        """Test transpile tool calls correct endpoint."""
        from coda_mcp import client

        mock_post.return_value = {
            "success": True,
            "target_framework": "cudaq",
            "converted_code": "# CUDA-Q code",
        }

        result = await client.transpile("qc = QuantumCircuit(2)", "cudaq")

        mock_post.assert_called_once_with(
            "/transpile",
            {
                "source_code": "qc = QuantumCircuit(2)",
                "target": "cudaq",
            },
        )
        assert result["success"] is True
        assert result["target_framework"] == "cudaq"

    @pytest.mark.asyncio
    async def test_simulate(self, mock_post):
        """Test simulate tool calls correct endpoint."""
        from coda_mcp import client

        mock_post.return_value = {
            "success": True,
            "method": "qasm",
            "counts": {"00": 50, "11": 50},
        }

        result = await client.simulate("code", method="qasm", shots=100)

        mock_post.assert_called_once()
        call_args = mock_post.call_args
        assert call_args[0][0] == "/simulate"
        assert call_args[0][1]["code"] == "code"
        assert call_args[0][1]["shots"] == 100
        assert result["method"] == "qasm"

    @pytest.mark.asyncio
    async def test_to_openqasm3(self, mock_post):
        """Test to_openqasm3 tool calls correct endpoint."""
        from coda_mcp import client

        mock_post.return_value = {
            "success": True,
            "openqasm3": "OPENQASM 3.0;",
        }

        result = await client.to_openqasm3("code")

        mock_post.assert_called_once_with("/to-openqasm3", {"code": "code"})
        assert "openqasm3" in result

    @pytest.mark.asyncio
    async def test_estimate_resources(self, mock_post):
        """Test estimate_resources tool calls correct endpoint."""
        from coda_mcp import client

        mock_post.return_value = {
            "success": True,
            "qubit_count": 2,
            "circuit_depth": 3,
        }

        result = await client.estimate_resources("code")

        mock_post.assert_called_once_with("/estimate-resources", {"code": "code"})
        assert result["qubit_count"] == 2

    @pytest.mark.asyncio
    async def test_split_circuit(self, mock_post):
        """Test split_circuit tool calls correct endpoint."""
        from coda_mcp import client

        mock_post.return_value = {
            "success": True,
            "subcircuits": [],
        }

        result = await client.split_circuit("code")

        mock_post.assert_called_once_with("/split-circuit", {"code": "code"})
        assert result["success"] is True


class TestQpuTools:
    """Tests for QPU tools."""

    @pytest.mark.asyncio
    async def test_qpu_submit(self, mock_post):
        """Test qpu_submit tool calls correct endpoint."""
        from coda_mcp import client

        mock_post.return_value = {
            "success": True,
            "job_id": "job-12345-abcde",
            "status": "running",
        }

        result = await client.qpu_submit("OPENQASM 3.0;", "ionq", 100)

        mock_post.assert_called_once_with(
            "/qpu/submit",
            {
                "openqasm3": "OPENQASM 3.0;",
                "backend": "ionq",
                "shots": 100,
                "accept_overage": False,
            },
        )
        assert result["status"] == "running"

    @pytest.mark.asyncio
    async def test_qpu_status(self, mock_post):
        """Test qpu_status tool calls correct endpoint."""
        from coda_mcp import client

        mock_post.return_value = {
            "success": True,
            "status": "completed",
            "results": {"00": 50, "11": 50},
        }

        result = await client.qpu_status("job-12345-abcde")

        mock_post.assert_called_once_with(
            "/qpu/status",
            {
                "job_id": "job-12345-abcde",
            },
        )
        assert result["status"] == "completed"

    @pytest.mark.asyncio
    async def test_qpu_devices(self, mock_get):
        """Test qpu_devices tool calls correct endpoint."""
        from coda_mcp import client

        mock_get.return_value = {
            "success": True,
            "devices": [{"name": "ionq", "id": "ionq-device-1"}],
            "max_shots": 10000,
        }

        result = await client.qpu_devices()

        mock_get.assert_called_once_with("/qpu/devices")
        assert len(result["devices"]) > 0

    @pytest.mark.asyncio
    async def test_qpu_estimate_cost(self, mock_post):
        """Test qpu_estimate_cost tool calls correct endpoint."""
        from coda_mcp import client

        mock_post.return_value = {
            "success": True,
            "estimated_cost_cents": 330,
            "backend": "ionq",
            "shots": 100,
        }

        result = await client.qpu_estimate_cost("ionq", 100)

        mock_post.assert_called_once_with(
            "/qpu/estimate-cost",
            {
                "backend": "ionq",
                "shots": 100,
            },
        )
        assert result["estimated_cost_cents"] == 330


class TestSearchTools:
    """Tests for search tools."""

    @pytest.mark.asyncio
    async def test_search_papers(self, mock_post):
        """Test search_papers tool calls correct endpoint."""
        from coda_mcp import client

        mock_post.return_value = {
            "success": True,
            "results": [{"title": "Quantum Paper", "url": "https://..."}],
        }

        await client.search_papers("quantum computing")

        mock_post.assert_called_once()
        call_args = mock_post.call_args
        assert call_args[0][0] == "/search"
        assert call_args[0][1]["query"] == "quantum computing"

    @pytest.mark.asyncio
    async def test_get_paper(self, mock_post):
        """Test get_paper tool calls correct endpoint."""
        from coda_mcp import client

        mock_post.return_value = {
            "success": True,
            "contents": [{"url": "https://...", "text": "Paper content"}],
        }

        await client.get_paper(["https://example.com"])

        mock_post.assert_called_once()
        call_args = mock_post.call_args
        assert call_args[0][0] == "/fetch"
        assert call_args[0][1]["urls"] == ["https://example.com"]


class TestQpuSubmitConfirmation:
    """Tests for QPU submission confirmation gate.

    The MCP server requires explicit confirmation before submitting to QPU.
    When unconfirmed, it returns cost estimate so users can make informed decisions.
    """

    @pytest.mark.asyncio
    async def test_without_confirmation_returns_cost_estimate(self, mock_post):
        """Unconfirmed submission returns cost estimate for user decision."""
        from coda_mcp.server import qpu_submit

        mock_post.return_value = {"estimated_cost_cents": 330}

        result = await qpu_submit("OPENQASM 3.0;", "ionq", 100)

        assert result["success"] is False
        assert result["requires_confirmation"] is True
        assert result["backend"] == "ionq"
        assert result["shots"] == 100
        assert result["estimated_cost_cents"] == 330

    @pytest.mark.asyncio
    async def test_cost_estimate_failure_returns_zero(self, mock_post):
        """Cost estimate failure doesn't block the rejection response."""
        from coda_mcp.server import qpu_submit

        mock_post.side_effect = Exception("API unreachable")

        result = await qpu_submit("OPENQASM 3.0;", "ionq", 100)

        assert result["success"] is False
        assert result["requires_confirmation"] is True
        assert result["estimated_cost_cents"] == 0  # Fail open

    @pytest.mark.asyncio
    async def test_with_confirmation_submits_to_backend(self, mock_post):
        """Confirmed submission calls backend with accept_overage=True."""
        from coda_mcp.server import qpu_submit

        mock_post.return_value = {"success": True, "job_id": "job-12345"}

        result = await qpu_submit("OPENQASM 3.0;", "ionq", 100, confirm_submission=True)

        assert result["success"] is True
        assert result["job_id"] == "job-12345"
        mock_post.assert_called_once_with(
            "/qpu/submit",
            {
                "openqasm3": "OPENQASM 3.0;",
                "backend": "ionq",
                "shots": 100,
                "accept_overage": True,
            },
        )


class TestServerModule:
    """Tests for the MCP server module."""

    def test_server_imports(self):
        """Verify the server module imports correctly."""
        from coda_mcp.server import mcp

        assert mcp.name == "coda"

    def test_type_aliases(self):
        """Verify type aliases are defined in server module."""
        from coda_mcp.server import (
            QpuBackend,
            QuantumFramework,
            SimulationBackend,
            SimulationMethod,
        )

        assert QuantumFramework is not None
        assert SimulationMethod is not None
        assert SimulationBackend is not None
        assert QpuBackend is not None
