"""
Session API Integration Tests

Tests for session-related HTTP API endpoints.
"""
import pytest
import asyncio
from httpx import AsyncClient


@pytest.mark.asyncio
class TestSessionsAPI:
    """Session API integration tests."""

    async def test_create_session(self, http_client: AsyncClient, test_template_id: str):
        """Test creating a new session."""
        print(f"http_client base_url: {http_client.base_url}")

        # First check if API is reachable
        health_resp = await http_client.get("/health")
        print(f"Health check: {health_resp.status_code}")

        response = await http_client.post(
            "/sessions",
            json={
                "template_id": test_template_id,
                "timeout": 300,
                "cpu": "1",
                "memory": "512Mi",
                "disk": "1Gi",
                "env_vars": {}
            }
        )

        if response.status_code not in (201, 200):
            print(f"Error response: {response.text}")

        assert response.status_code in (201, 200)
        data = response.json()
        assert "id" in data
        assert data["template_id"] == test_template_id
        # Session may be in any of these states depending on creation speed
        assert data["status"] in ("creating", "starting", "initializing", "running", "ready")

        # Cleanup - terminate session
        session_id = data["id"]
        await http_client.post(f"/sessions/{session_id}/terminate")

    async def test_create_session_with_defaults(self, http_client: AsyncClient, test_template_id: str):
        """Test creating a session with default resource values."""
        response = await http_client.post(
            "/sessions",
            json={
                "template_id": test_template_id,
                "timeout": 300
            }
        )

        assert response.status_code in (201, 200)
        data = response.json()
        assert "id" in data
        assert data["template_id"] == test_template_id

        # Cleanup - terminate session
        session_id = data["id"]
        await http_client.post(f"/sessions/{session_id}/terminate")

    async def test_create_session_invalid_template(self, http_client: AsyncClient):
        """Test using non-existent template to create session."""
        response = await http_client.post(
            "/sessions",
            json={
                "template_id": "non-existent-template-xyz",
                "timeout": 300
            }
        )

        # API returns 400 for validation errors (template not found is a validation error)
        assert response.status_code == 400

    async def test_create_session_invalid_timeout(self, http_client: AsyncClient, test_template_id: str):
        """Test using invalid timeout value to create session."""
        response = await http_client.post(
            "/sessions",
            json={
                "template_id": test_template_id,
                "timeout": 5000  # Exceeds MAX_TIMEOUT of 3600
            }
        )

        assert response.status_code == 422  # Validation error

    async def test_get_session(self, http_client: AsyncClient, test_session_id: str):
        """Test getting session details."""
        response = await http_client.get(f"/sessions/{test_session_id}")

        assert response.status_code == 200
        data = response.json()
        assert data["id"] == test_session_id
        assert "status" in data
        assert "template_id" in data
        assert "created_at" in data

    async def test_get_session_not_found(self, http_client: AsyncClient):
        """Test getting a session that doesn't exist."""
        response = await http_client.get("/sessions/non-existent-session-id")

        assert response.status_code == 404

    async def test_list_sessions(self, http_client: AsyncClient):
        """Test listing all sessions."""
        response = await http_client.get("/sessions")
        assert response.status_code == 200

        data = response.json()
        assert "items" in data
        assert "total" in data
        assert "limit" in data
        assert "offset" in data
        assert "has_more" in data
        assert isinstance(data["items"], list)

    async def test_list_sessions_with_status_filter(self, http_client: AsyncClient):
        """Test listing sessions with status filter."""
        response = await http_client.get("/sessions?status=running")
        assert response.status_code == 200

        data = response.json()
        assert "items" in data
        # Verify all returned sessions have the requested status
        for session in data["items"]:
            assert session["status"] == "running"

    async def test_list_sessions_with_template_filter(self, http_client: AsyncClient):
        """Test listing sessions with template_id filter."""
        response = await http_client.get("/sessions?template_id=python3.11-baseline")
        assert response.status_code == 200

        data = response.json()
        assert "items" in data
        # Verify all returned sessions have the requested template
        for session in data["items"]:
            assert session["template_id"] == "python3.11-baseline"

    async def test_list_sessions_with_pagination(self, http_client: AsyncClient):
        """Test listing sessions with pagination."""
        # First page with limit=2
        response = await http_client.get("/sessions?limit=2&offset=0")
        assert response.status_code == 200

        data = response.json()
        assert "items" in data
        assert data["limit"] == 2
        assert data["offset"] == 0
        assert len(data["items"]) <= 2

        # Second page
        response2 = await http_client.get("/sessions?limit=2&offset=2")
        assert response2.status_code == 200

        data2 = response2.json()
        assert data2["limit"] == 2
        assert data2["offset"] == 2

        # Verify has_more flag
        if data["total"] > 2:
            assert data["has_more"] is True

    async def test_list_sessions_limit_validation(self, http_client: AsyncClient):
        """Test that limit is properly validated (max 200)."""
        # Request limit > 200, should be clamped to 200
        response = await http_client.get("/sessions?limit=300")
        assert response.status_code == 200

        data = response.json()
        assert data["limit"] == 200

    async def test_terminate_session(self, http_client: AsyncClient, test_template_id: str):
        """Test terminating a session."""
        # Create a session first
        create_response = await http_client.post(
            "/sessions",
            json={
                "template_id": test_template_id,
                "timeout": 300
            }
        )
        assert create_response.status_code in (201, 200)
        session = create_response.json()
        session_id = session["id"]

        # Wait for session to be ready
        for _ in range(30):
            response = await http_client.get(f"/sessions/{session_id}")
            if response.status_code == 200:
                session_data = response.json()
                if session_data["status"] in ("running", "ready"):
                    break
            await asyncio.sleep(1)

        # Terminate the session using POST /terminate endpoint
        terminate_response = await http_client.post(f"/sessions/{session_id}/terminate")

        assert terminate_response.status_code == 200
        data = terminate_response.json()
        assert data["status"] in ("terminated", "terminating")

        # Verify session record still exists after terminate
        get_response = await http_client.get(f"/sessions/{session_id}")
        assert get_response.status_code == 200

        # Clean up - delete the session permanently
        await http_client.delete(f"/sessions/{session_id}")

        # Verify session is now deleted (returns 404)
        get_response_after_delete = await http_client.get(f"/sessions/{session_id}")
        assert get_response_after_delete.status_code == 404

    async def test_session_container_created(
        self,
        http_client: AsyncClient,
        test_session_id: str
    ):
        """Test that container is created when session is created."""
        response = await http_client.get(f"/sessions/{test_session_id}")

        assert response.status_code == 200
        data = response.json()
        assert data["id"] == test_session_id

        # Container should be created or assigned for the session
        if "container_id" in data and data["container_id"]:
            assert data["container_id"]
        # Check that session has a status indicating container is ready
        assert data["status"] in ("running", "ready", "creating")

    async def test_session_container_removed(
        self,
        http_client: AsyncClient,
        test_template_id: str
    ):
        """Test that container is removed when session is terminated."""
        # Create a session
        create_response = await http_client.post(
            "/sessions",
            json={
                "template_id": test_template_id,
                "timeout": 300
            }
        )
        session = create_response.json()
        session_id = session["id"]
        container_id = session.get("container_id")

        # Wait for session to be ready
        for _ in range(30):
            response = await http_client.get(f"/sessions/{session_id}")
            if response.status_code == 200:
                session_data = response.json()
                if session_data.get("container_id"):
                    container_id = session_data["container_id"]
                if session_data["status"] in ("running", "ready"):
                    break
            await asyncio.sleep(1)

        # Terminate the session using POST /terminate endpoint
        await http_client.post(f"/sessions/{session_id}/terminate")

        # Wait for termination
        for _ in range(10):
            response = await http_client.get(f"/sessions/{session_id}")
            if response.status_code == 200:
                session_data = response.json()
                if session_data["status"] == "terminated":
                    break
            await asyncio.sleep(0.5)

        # Verify session is terminated
        response = await http_client.get(f"/sessions/{session_id}")
        if response.status_code == 200:
            session_data = response.json()
            assert session_data["status"] == "terminated"

        # Clean up - delete the session permanently
        await http_client.delete(f"/sessions/{session_id}")

    async def test_session_with_custom_env_vars(
        self,
        http_client: AsyncClient,
        test_template_id: str
    ):
        """Test creating session with custom environment variables."""
        env_vars = {
            "CUSTOM_VAR_1": "value1",
            "CUSTOM_VAR_2": "value2"
        }

        response = await http_client.post(
            "/sessions",
            json={
                "template_id": test_template_id,
                "timeout": 300,
                "env_vars": env_vars
            }
        )

        assert response.status_code in (201, 200)
        data = response.json()
        assert data["id"]

        # Cleanup - terminate session
        session_id = data["id"]
        await http_client.post(f"/sessions/{session_id}/terminate")

    async def test_session_with_custom_resources(
        self,
        http_client: AsyncClient,
        test_template_id: str
    ):
        """Test creating session with custom resource limits."""
        response = await http_client.post(
            "/sessions",
            json={
                "template_id": test_template_id,
                "timeout": 300,
                "cpu": "2",
                "memory": "1Gi",
                "disk": "5Gi"
            }
        )

        assert response.status_code in (201, 200)
        data = response.json()
        assert data["id"]

        # Cleanup - terminate session
        session_id = data["id"]
        await http_client.post(f"/sessions/{session_id}/terminate")

    async def test_health_check(self, http_client: AsyncClient):
        """Test health check API."""
        response = await http_client.get("/health")

        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "healthy"
        assert "version" in data
        assert "uptime" in data

    async def test_create_persistent_session(
        self,
        http_client: AsyncClient,
        test_template_id: str
    ):
        """Test creating a persistent session."""
        response = await http_client.post(
            "/sessions",
            json={
                "template_id": test_template_id,
                "timeout": 300,
                "mode": "persistent"
            }
        )

        assert response.status_code in (201, 200)
        data = response.json()
        assert data["id"]
        # Persistent sessions should have mode field or be distinguishable
        # (implementation may vary)

        # Cleanup - terminate session
        session_id = data["id"]
        await http_client.post(f"/sessions/{session_id}/terminate")


@pytest.mark.asyncio
class TestSessionsAPIWithExecution:
    """Session API tests that involve execution."""

    async def test_session_execution_workflow(
        self,
        http_client: AsyncClient,
        test_template_id: str
    ):
        """Test creating session and executing code in it."""
        # Create session
        session_response = await http_client.post(
            "/sessions",
            json={
                "template_id": test_template_id,
                "timeout": 300
            }
        )
        assert session_response.status_code in (201, 200)
        session = session_response.json()
        session_id = session["id"]

        # Wait for session to be ready
        for _ in range(30):
            response = await http_client.get(f"/sessions/{session_id}")
            if response.status_code == 200:
                session_data = response.json()
                if session_data["status"] in ("running", "ready"):
                    break
            await asyncio.sleep(1)
        else:
            pytest.fail("Session did not become ready")

        # Execute code
        execution_response = await http_client.post(
            f"/executions/sessions/{session_id}/execute",
            json={
                "code": 'def handler(event):\n    return {"message": "Session execution test"}',
                "language": "python",
                "timeout": 10
            }
        )
        assert execution_response.status_code in (201, 200)

        # Cleanup - terminate session
        await http_client.post(f"/sessions/{session_id}/terminate")


@pytest.mark.asyncio
class TestSessionStatusTransitions:
    """Session status transition tests."""

    async def test_session_status_creating_when_starting(
        self,
        http_client: AsyncClient,
        test_template_id: str
    ):
        """Test that session status is 'creating' when session is starting."""
        response = await http_client.post(
            "/sessions",
            json={
                "template_id": test_template_id,
                "timeout": 300
            }
        )

        assert response.status_code in (201, 200)
        data = response.json()
        session_id = data["id"]

        # Immediately check status - should be 'creating' when starting
        status_response = await http_client.get(f"/sessions/{session_id}")
        assert status_response.status_code == 200
        session_data = status_response.json()

        # Session status should be 'creating' when just starting
        assert session_data["status"] == "creating", f"Expected 'creating' status, got '{session_data['status']}'"

        # Cleanup - terminate session
        await http_client.post(f"/sessions/{session_id}/terminate")

    async def test_session_status_running_when_startup_succeeds(
        self,
        http_client: AsyncClient,
        test_template_id: str
    ):
        """Test that session status becomes 'running' when startup succeeds."""
        response = await http_client.post(
            "/sessions",
            json={
                "template_id": test_template_id,
                "timeout": 300
            }
        )

        assert response.status_code in (201, 200)
        data = response.json()
        session_id = data["id"]

        # Wait for session to become running (startup succeeds)
        for i in range(60):
            status_response = await http_client.get(f"/sessions/{session_id}")
            if status_response.status_code == 200:
                session_data = status_response.json()
                status = session_data["status"]
                if status == "running":
                    # Session successfully reached running state
                    assert status == "running"
                    break
                elif status == "failed":
                    pytest.fail(f"Session failed to start: {session_data}")
            await asyncio.sleep(1)
        else:
            pytest.fail("Session did not reach 'running' status within timeout")

        # Cleanup - terminate session
        await http_client.post(f"/sessions/{session_id}/terminate")
