"""E2E Basic Function Tests — REST API endpoints.

Verifies all API routes work correctly with proper request/response handling.

Test categories:
  1. Health & metrics
  2. Agent CRUD lifecycle
  3. Skill registration & listing
  4. Task submission (sync/async/SSE)
  5. Chat session lifecycle
  6. LLM usage tracking
  7. Error handling & edge cases
"""

import pytest
import httpx

from tests.e2e.conftest import register_skill_via_api, create_session_via_api


# ═══════════════════════════════════════════════════════════════════════════
# 1. Health & Metrics
# ═══════════════════════════════════════════════════════════════════════════


@pytest.mark.e2e_basic
class TestHealthAPI:
    def test_health_returns_ok(self, api_client: httpx.Client):
        resp = api_client.get("/api/v1/health")
        assert resp.status_code == 200
        data = resp.json()
        assert data.get("status") in ("ok", "healthy")

    def test_metrics_endpoint(self, api_client: httpx.Client):
        resp = api_client.get("/api/v1/metrics")
        assert resp.status_code == 200


# ═══════════════════════════════════════════════════════════════════════════
# 2. Agent CRUD Lifecycle
# ═══════════════════════════════════════════════════════════════════════════


@pytest.mark.e2e_basic
class TestAgentCRUD:
    """Full Agent CRUD lifecycle: create → list → get → delete."""

    def test_create_agent_from_skill(self, api_client: httpx.Client):
        register_skill_via_api(api_client, "crud_skill", keywords=["crud"])
        resp = api_client.post("/api/v1/agents", json={"skill_name": "crud_skill"})
        assert resp.status_code == 201
        data = resp.json()
        assert data["name"] == "crud_skill"

    def test_list_agents(self, api_client: httpx.Client):
        register_skill_via_api(api_client, "list_skill", keywords=["list_agent"])
        api_client.post("/api/v1/agents", json={"skill_name": "list_skill"})
        resp = api_client.get("/api/v1/agents")
        assert resp.status_code == 200
        agents = resp.json()
        assert isinstance(agents, list)
        assert any(a["name"] == "list_skill" for a in agents)

    def test_get_agent_detail(self, api_client: httpx.Client):
        register_skill_via_api(api_client, "detail_skill", keywords=["detail"])
        api_client.post("/api/v1/agents", json={"skill_name": "detail_skill"})
        resp = api_client.get("/api/v1/agents/detail_skill")
        assert resp.status_code == 200
        data = resp.json()
        assert data["name"] == "detail_skill"

    def test_delete_agent(self, api_client: httpx.Client):
        register_skill_via_api(api_client, "delete_skill", keywords=["delete_agent"])
        api_client.post("/api/v1/agents", json={"skill_name": "delete_skill"})
        resp = api_client.delete("/api/v1/agents/delete_skill")
        assert resp.status_code == 204
        # Verify deleted
        resp = api_client.get("/api/v1/agents/delete_skill")
        assert resp.status_code == 404

    def test_create_agent_nonexistent_skill(self, api_client: httpx.Client):
        resp = api_client.post("/api/v1/agents", json={"skill_name": "nonexistent_skill_xyz"})
        assert resp.status_code in (400, 404)

    def test_get_nonexistent_agent(self, api_client: httpx.Client):
        resp = api_client.get("/api/v1/agents/does_not_exist")
        assert resp.status_code == 404


# ═══════════════════════════════════════════════════════════════════════════
# 3. Skill Registration & Listing
# ═══════════════════════════════════════════════════════════════════════════


@pytest.mark.e2e_basic
class TestSkillAPI:
    def test_register_skill(self, api_client: httpx.Client):
        resp = register_skill_via_api(api_client, "reg_skill", keywords=["reg"])
        assert resp.status_code == 201

    def test_list_skills(self, api_client: httpx.Client):
        register_skill_via_api(api_client, "list_test_skill", keywords=["list_test"])
        resp = api_client.get("/api/v1/skills")
        assert resp.status_code == 200
        skills = resp.json()
        assert isinstance(skills, list)
        assert len(skills) >= 1

    def test_register_duplicate_skill(self, api_client: httpx.Client):
        register_skill_via_api(api_client, "dup_skill", keywords=["dup"])
        resp = register_skill_via_api(api_client, "dup_skill", keywords=["dup"])
        # Should either overwrite or return conflict
        assert resp.status_code in (200, 201, 409)

    def test_skill_with_execution_mode(self, api_client: httpx.Client):
        resp = register_skill_via_api(
            api_client, "react_skill", keywords=["react_test"], execution_mode="react"
        )
        assert resp.status_code == 201

    def test_skill_mention_suggest(self, api_client: httpx.Client):
        register_skill_via_api(api_client, "mention_skill", keywords=["mention_test"])
        resp = api_client.get("/api/v1/skills/mention-suggest", params={"q": "mention"})
        assert resp.status_code == 200


# ═══════════════════════════════════════════════════════════════════════════
# 4. Task Submission
# ═══════════════════════════════════════════════════════════════════════════


@pytest.mark.e2e_basic
class TestTaskAPI:
    def test_submit_task_sync(self, api_client: httpx.Client):
        register_skill_via_api(api_client, "sync_task_skill", keywords=["sync_task"])
        resp = api_client.post(
            "/api/v1/tasks",
            json={
                "input_data": {"query": "test sync task"},
                "skill_name": "sync_task_skill",
            },
        )
        assert resp.status_code == 200
        data = resp.json()
        assert "output" in data or "data" in data or "skill_name" in data

    def test_submit_task_with_agent_name(self, api_client: httpx.Client):
        register_skill_via_api(api_client, "agent_task_skill", keywords=["agent_task"])
        api_client.post("/api/v1/agents", json={"skill_name": "agent_task_skill"})
        resp = api_client.post(
            "/api/v1/tasks",
            json={
                "input_data": {"query": "test agent task"},
                "agent_name": "agent_task_skill",
            },
        )
        assert resp.status_code == 200

    def test_submit_task_auto_route(self, api_client: httpx.Client):
        register_skill_via_api(api_client, "auto_route_skill", keywords=["auto_route"])
        resp = api_client.post(
            "/api/v1/tasks",
            json={"input_data": {"query": "Please auto_route this for me"}},
        )
        assert resp.status_code == 200

    def test_list_tasks(self, api_client: httpx.Client):
        resp = api_client.get("/api/v1/tasks")
        assert resp.status_code == 200

    def test_submit_task_missing_data(self, api_client: httpx.Client):
        resp = api_client.post("/api/v1/tasks", json={})
        # Should return 400 or 422
        assert resp.status_code in (400, 422)


# ═══════════════════════════════════════════════════════════════════════════
# 5. Chat Session Lifecycle
# ═══════════════════════════════════════════════════════════════════════════


@pytest.mark.e2e_basic
class TestChatSessionAPI:
    def test_create_session(self, api_client: httpx.Client):
        session_id = create_session_via_api(api_client)
        assert session_id is not None
        assert len(session_id) > 0

    def test_list_sessions(self, api_client: httpx.Client):
        create_session_via_api(api_client)
        resp = api_client.get("/api/v1/chat/sessions")
        assert resp.status_code == 200
        sessions = resp.json()
        assert isinstance(sessions, list)
        assert len(sessions) >= 1

    def test_get_session(self, api_client: httpx.Client):
        session_id = create_session_via_api(api_client)
        resp = api_client.get(f"/api/v1/chat/sessions/{session_id}")
        assert resp.status_code == 200

    def test_session_messages(self, api_client: httpx.Client):
        session_id = create_session_via_api(api_client)
        # Send a message
        resp = api_client.post(
            f"/api/v1/chat/sessions/{session_id}/messages",
            json={"content": "Hello from e2e test"},
        )
        assert resp.status_code == 200
        # Get messages
        resp = api_client.get(f"/api/v1/chat/sessions/{session_id}/messages")
        assert resp.status_code == 200

    def test_close_session(self, api_client: httpx.Client):
        session_id = create_session_via_api(api_client)
        resp = api_client.delete(f"/api/v1/chat/sessions/{session_id}")
        assert resp.status_code == 200


# ═══════════════════════════════════════════════════════════════════════════
# 6. LLM Usage Tracking
# ═══════════════════════════════════════════════════════════════════════════


@pytest.mark.e2e_basic
class TestLLMUsageAPI:
    def test_llm_usage_endpoint(self, api_client: httpx.Client):
        resp = api_client.get("/api/v1/llm/usage")
        assert resp.status_code == 200

    def test_llm_usage_after_task(self, api_client: httpx.Client):
        register_skill_via_api(api_client, "usage_track_skill", keywords=["usage_track"])
        api_client.post(
            "/api/v1/tasks",
            json={
                "input_data": {"query": "test usage tracking"},
                "skill_name": "usage_track_skill",
            },
        )
        resp = api_client.get("/api/v1/llm/usage")
        assert resp.status_code == 200


# ═══════════════════════════════════════════════════════════════════════════
# 7. Error Handling & Edge Cases
# ═══════════════════════════════════════════════════════════════════════════


@pytest.mark.e2e_basic
class TestAPIErrorHandling:
    def test_404_for_unknown_route(self, api_client: httpx.Client):
        resp = api_client.get("/api/v1/nonexistent_route")
        assert resp.status_code == 404

    def test_invalid_json_body(self, api_client: httpx.Client):
        resp = api_client.post(
            "/api/v1/tasks",
            content=b"not json",
            headers={"Content-Type": "application/json"},
        )
        assert resp.status_code in (400, 422)

    def test_missing_api_key(self, e2e_server: str):
        """Requests without API key should be rejected (if auth enabled)."""
        client = httpx.Client(base_url=e2e_server, timeout=10)
        resp = client.get("/api/v1/agents")
        # Should be 401/403 or still 200 if auth is not enforced on this endpoint
        assert resp.status_code in (200, 401, 403)

    def test_invalid_api_key(self, e2e_server: str):
        client = httpx.Client(
            base_url=e2e_server,
            headers={"X-API-Key": "invalid_key"},
            timeout=10,
        )
        resp = client.get("/api/v1/agents")
        assert resp.status_code in (200, 401, 403)
