"""Tests for acli-mcp-server."""

import json
import pytest
from unittest.mock import patch, AsyncMock

from acli_mcp_server.server import call_acli, suggest_acli_commands, _acli_available, _run_acli


@pytest.mark.asyncio
async def test_call_acli_prepends_acli():
    """Commands without 'acli ' prefix get it added."""
    with patch("acli_mcp_server.server._run_acli", new_callable=AsyncMock) as mock:
        mock.return_value = {"command": "acli jira project list", "exit_code": 0, "output": ""}
        await call_acli("jira project list")
        mock.assert_called_once_with("jira project list", 60)


@pytest.mark.asyncio
async def test_call_acli_returns_json_string():
    """call_acli returns a JSON-serialized string."""
    with patch("acli_mcp_server.server._run_acli", new_callable=AsyncMock) as mock:
        mock.return_value = {"command": "acli jira project list", "exit_code": 0, "output": "stuff"}
        result = await call_acli("acli jira project list")
        parsed = json.loads(result)
        assert parsed["exit_code"] == 0
        assert parsed["output"] == "stuff"


@pytest.mark.asyncio
async def test_run_acli_no_cli():
    """Returns error when acli is not installed."""
    with patch("acli_mcp_server.server._acli_available", return_value=False):
        result = await _run_acli("acli jira project list")
        assert "error" in result
        assert "not installed" in result["error"]


@pytest.mark.asyncio
async def test_run_acli_success():
    """Successful command returns parsed JSON output."""
    mock_proc = AsyncMock()
    mock_proc.returncode = 0
    mock_proc.communicate.return_value = (b'[{"key":"PROJ-1"}]', b"")

    with patch("acli_mcp_server.server._acli_available", return_value=True), \
         patch("asyncio.create_subprocess_exec", return_value=mock_proc):
        result = await _run_acli("acli jira workitem list --site test.atlassian.net --project PROJ")
        assert result["exit_code"] == 0
        assert result["output"] == [{"key": "PROJ-1"}]


@pytest.mark.asyncio
async def test_run_acli_plain_text_output():
    """Non-JSON output is returned as plain string."""
    mock_proc = AsyncMock()
    mock_proc.returncode = 0
    mock_proc.communicate.return_value = (b"some plain text", b"")

    with patch("acli_mcp_server.server._acli_available", return_value=True), \
         patch("asyncio.create_subprocess_exec", return_value=mock_proc):
        result = await _run_acli("acli jira project list")
        assert result["output"] == "some plain text"


@pytest.mark.asyncio
async def test_run_acli_command_failure():
    """Failed command includes error message."""
    mock_proc = AsyncMock()
    mock_proc.returncode = 1
    mock_proc.communicate.return_value = (b"", b"not found")

    with patch("acli_mcp_server.server._acli_available", return_value=True), \
         patch("asyncio.create_subprocess_exec", return_value=mock_proc):
        result = await _run_acli("acli jira workitem view --workitem FAKE-999")
        assert result["exit_code"] == 1
        assert "not found" in result["error"]


@pytest.mark.asyncio
async def test_run_acli_blocks_shell_injection():
    """Shell operators are blocked."""
    with patch("acli_mcp_server.server._acli_available", return_value=True):
        result = await _run_acli("acli jira project list; rm -rf /")
        assert "error" in result
        assert "shell operator" in result["error"]


@pytest.mark.asyncio
async def test_run_acli_blocks_pipe():
    """Pipe operator is blocked."""
    with patch("acli_mcp_server.server._acli_available", return_value=True):
        result = await _run_acli("acli jira project list | cat")
        assert "error" in result
        assert "shell operator" in result["error"]


@pytest.mark.asyncio
async def test_suggest_acli_commands_workitem():
    """Suggests workitem commands for workitem-related queries."""
    result = await suggest_acli_commands("create a workitem")
    parsed = json.loads(result)
    assert any("workitem" in s for s in parsed["suggestions"])


@pytest.mark.asyncio
async def test_suggest_acli_commands_fallback():
    """Returns all categories when no match found."""
    result = await suggest_acli_commands("xyzzy nonsense")
    parsed = json.loads(result)
    assert len(parsed["suggestions"]) > 5


def test_acli_available():
    """_acli_available returns bool based on shutil.which."""
    with patch("shutil.which", return_value="/usr/local/bin/acli"):
        assert _acli_available() is True
    with patch("shutil.which", return_value=None):
        assert _acli_available() is False
