"""
Unit tests for the agent server module.

Tests for the HTTP server that handles agent requests.
Note: These tests don't require FastAPI to be installed for basic checks.
"""

from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, patch

import pytest


def _fastapi_available() -> bool:
    """Check if FastAPI is available."""
    try:
        import fastapi  # noqa: F401

        return True
    except ImportError:
        return False


class TestServerModule:
    """Tests for server module basics."""

    def test_fastapi_availability_check(self) -> None:
        """Test that FASTAPI_AVAILABLE flag is set correctly."""
        from persuade.agents import server

        # The flag should be a boolean
        assert isinstance(server.FASTAPI_AVAILABLE, bool)

    def test_create_app_without_fastapi(self) -> None:
        """Test create_app raises ImportError when FastAPI not available."""
        from persuade.agents import server

        if server.FASTAPI_AVAILABLE:
            pytest.skip("FastAPI is installed, skipping this test")

        with pytest.raises(ImportError, match="FastAPI is not installed"):
            server.create_app()


@pytest.mark.skipif(
    not _fastapi_available(),
    reason="FastAPI not installed",
)
class TestServerWithFastAPI:
    """Tests that require FastAPI to be installed."""

    def test_create_app_returns_fastapi_instance(self) -> None:
        """Test that create_app returns a FastAPI instance."""
        from fastapi import FastAPI

        from persuade.agents.server import create_app

        app = create_app()
        assert isinstance(app, FastAPI)

    def test_app_has_health_endpoint(self) -> None:
        """Test that app has health check endpoint."""
        from fastapi.testclient import TestClient

        from persuade.agents.server import create_app

        app = create_app()
        client = TestClient(app)

        response = client.get("/health")
        assert response.status_code == 200

        data = response.json()
        assert data["status"] == "healthy"
        assert "timestamp" in data
        assert "iterate-pr" in data["agents_available"]
        assert "warnings" in data
        assert len(data["warnings"]) > 0  # Should have production warning

    def test_run_agent_missing_agent_type(self) -> None:
        """Test error when agent_type is missing."""
        from fastapi.testclient import TestClient

        from persuade.agents.server import create_app

        app = create_app()
        client = TestClient(app)

        response = client.post("/agent/run", json={"params": {}})
        assert response.status_code == 422  # Validation error

    def test_run_agent_unknown_type(self) -> None:
        """Test error when agent_type is unknown."""
        from fastapi.testclient import TestClient

        from persuade.agents.server import create_app

        app = create_app()
        client = TestClient(app)

        response = client.post(
            "/agent/run",
            json={"agent_type": "unknown-agent", "params": {}},
        )
        assert response.status_code == 400
        assert "Unknown agent type" in response.json()["detail"]

    def test_run_agent_iterate_pr_missing_pr_number(self) -> None:
        """Test error when pr_number is missing for iterate-pr."""
        from fastapi.testclient import TestClient

        from persuade.agents.server import create_app

        app = create_app()
        client = TestClient(app)

        response = client.post(
            "/agent/run",
            json={"agent_type": "iterate-pr", "params": {}},
        )
        assert response.status_code == 400
        assert "pr_number" in response.json()["detail"]

    @patch("persuade.agents.iterate_pr.IteratePRAgent")
    def test_run_agent_plan_only(self, mock_agent_class: MagicMock, tmp_path: Path) -> None:
        """Test running agent in plan-only mode (no auto_approve)."""
        from fastapi.testclient import TestClient

        from persuade.agents.base import AgentPhaseResult
        from persuade.agents.server import create_app

        # Mock the agent with AsyncMock for async methods
        mock_agent = MagicMock()
        mock_agent.plan = AsyncMock(
            return_value=AgentPhaseResult(
                success=True,
                data={"items": 3},
                messages=["Found 3 feedback items"],
            )
        )
        mock_agent_class.return_value = mock_agent

        app = create_app()
        client = TestClient(app)

        response = client.post(
            "/agent/run",
            json={
                "agent_type": "iterate-pr",
                "params": {"pr_number": 67},
                "auto_approve": False,
            },
        )

        assert response.status_code == 200
        data = response.json()
        assert data["success"] is True
        assert data["phase"] == "plan"
        assert "request_id" in data
        assert any("POST to /agent/" in msg for msg in data["messages"])

    @patch("persuade.agents.iterate_pr.IteratePRAgent")
    def test_run_agent_auto_approve(self, mock_agent_class: MagicMock, tmp_path: Path) -> None:
        """Test running agent with auto_approve (runs both phases)."""
        from fastapi.testclient import TestClient

        from persuade.agents.base import AgentPhaseResult
        from persuade.agents.server import create_app

        # Mock the agent with AsyncMock for async methods
        mock_agent = MagicMock()
        mock_agent.plan = AsyncMock(
            return_value=AgentPhaseResult(
                success=True,
                data={"items": 3},
                messages=["Found 3 feedback items"],
            )
        )
        mock_agent.execute = AsyncMock(
            return_value=AgentPhaseResult(
                success=True,
                data={},
                messages=["Applied changes"],
            )
        )
        mock_agent_class.return_value = mock_agent

        app = create_app()
        client = TestClient(app)

        response = client.post(
            "/agent/run",
            json={
                "agent_type": "iterate-pr",
                "params": {"pr_number": 67},
                "auto_approve": True,
            },
        )

        assert response.status_code == 200
        data = response.json()
        assert data["success"] is True
        assert data["phase"] == "execute"
        mock_agent.execute.assert_called_once()

    def test_get_pending_plan_not_found(self) -> None:
        """Test getting a non-existent pending plan."""
        from fastapi.testclient import TestClient

        from persuade.agents.server import create_app

        app = create_app()
        client = TestClient(app)

        response = client.get("/agent/nonexistent")
        assert response.status_code == 404

    def test_execute_pending_not_found(self) -> None:
        """Test executing a non-existent pending plan."""
        from fastapi.testclient import TestClient

        from persuade.agents.server import create_app

        app = create_app()
        client = TestClient(app)

        response = client.post("/agent/nonexistent/execute")
        assert response.status_code == 404

    def test_cancel_pending_not_found(self) -> None:
        """Test cancelling a non-existent pending plan."""
        from fastapi.testclient import TestClient

        from persuade.agents.server import create_app

        app = create_app()
        client = TestClient(app)

        response = client.delete("/agent/nonexistent")
        assert response.status_code == 404

    @patch("persuade.agents.iterate_pr.IteratePRAgent")
    def test_pending_plan_lifecycle(self, mock_agent_class: MagicMock, tmp_path: Path) -> None:
        """Test full lifecycle: plan -> get -> execute -> removed from memory."""
        from fastapi.testclient import TestClient

        from persuade.agents.base import AgentPhaseResult
        from persuade.agents.server import create_app

        # Mock the agent with AsyncMock for async methods
        mock_agent = MagicMock()
        mock_agent.plan = AsyncMock(
            return_value=AgentPhaseResult(
                success=True,
                data={"items": 3},
                messages=["Found 3 feedback items"],
            )
        )
        mock_agent.execute = AsyncMock(
            return_value=AgentPhaseResult(
                success=True,
                data={},
                messages=["Applied changes"],
            )
        )
        mock_agent_class.return_value = mock_agent

        app = create_app()
        client = TestClient(app)

        # 1. Create plan (without auto_approve)
        response = client.post(
            "/agent/run",
            json={
                "agent_type": "iterate-pr",
                "params": {"pr_number": 123},
                "auto_approve": False,
                "project_dir": str(tmp_path),
            },
        )
        assert response.status_code == 200
        data = response.json()
        assert data["phase"] == "plan"
        request_id = data["request_id"]

        # 2. GET to verify plan is pending
        response = client.get(f"/agent/{request_id}")
        assert response.status_code == 200
        assert response.json()["request_id"] == request_id

        # 3. Execute the pending plan
        response = client.post(f"/agent/{request_id}/execute")
        assert response.status_code == 200
        assert response.json()["phase"] == "execute"

        # 4. Verify plan is removed (should return 404)
        response = client.get(f"/agent/{request_id}")
        assert response.status_code == 404
