import json

import httpx
import pytest
import respx

from whatsrb_cloud.connection import Connection, API_PREFIX
from whatsrb_cloud.errors import (
    AuthenticationError,
    ForbiddenError,
    NotFoundError,
    ValidationError,
    ConflictError,
    RateLimitError,
    ServerError,
    WhatsrbCloudError,
)

BASE_URL = "https://whatsrb.com"


@pytest.fixture
def conn():
    return Connection(api_key="wrb_test_xxx", base_url=BASE_URL, timeout=10)


class TestConnection:
    @respx.mock
    def test_get_success(self, conn):
        respx.get(f"{BASE_URL}{API_PREFIX}/agents").mock(
            return_value=httpx.Response(200, json={"data": [], "meta": {}})
        )
        result = conn.get("/agents")
        assert result == {"data": [], "meta": {}}

    @respx.mock
    def test_post_success(self, conn):
        respx.post(f"{BASE_URL}{API_PREFIX}/agents").mock(
            return_value=httpx.Response(201, json={"data": {"id": "agt_1"}})
        )
        result = conn.post("/agents", {"agent": {"name": "Test"}})
        assert result["data"]["id"] == "agt_1"

    @respx.mock
    def test_patch_success(self, conn):
        respx.patch(f"{BASE_URL}{API_PREFIX}/agents/agt_1").mock(
            return_value=httpx.Response(200, json={"data": {"id": "agt_1", "name": "Updated"}})
        )
        result = conn.patch("/agents/agt_1", {"agent": {"name": "Updated"}})
        assert result["data"]["name"] == "Updated"

    @respx.mock
    def test_delete_success(self, conn):
        respx.delete(f"{BASE_URL}{API_PREFIX}/agents/agt_1").mock(
            return_value=httpx.Response(204, text="")
        )
        result = conn.delete("/agents/agt_1")
        assert result is None

    @respx.mock
    def test_authentication_error(self, conn):
        respx.get(f"{BASE_URL}{API_PREFIX}/agents").mock(
            return_value=httpx.Response(401, json={"error": "Invalid API key"})
        )
        with pytest.raises(AuthenticationError) as exc:
            conn.get("/agents")
        assert exc.value.status == 401

    @respx.mock
    def test_forbidden_error(self, conn):
        respx.get(f"{BASE_URL}{API_PREFIX}/agents").mock(
            return_value=httpx.Response(403, json={"error": "Forbidden"})
        )
        with pytest.raises(ForbiddenError):
            conn.get("/agents")

    @respx.mock
    def test_not_found_error(self, conn):
        respx.get(f"{BASE_URL}{API_PREFIX}/agents/agt_xxx").mock(
            return_value=httpx.Response(404, json={"error": "Not found"})
        )
        with pytest.raises(NotFoundError):
            conn.get("/agents/agt_xxx")

    @respx.mock
    def test_validation_error(self, conn):
        respx.post(f"{BASE_URL}{API_PREFIX}/agents").mock(
            return_value=httpx.Response(422, json={"error": "Invalid", "details": {"name": ["required"]}})
        )
        with pytest.raises(ValidationError) as exc:
            conn.post("/agents", {"agent": {}})
        assert exc.value.details == {"name": ["required"]}

    @respx.mock
    def test_conflict_error(self, conn):
        respx.post(f"{BASE_URL}{API_PREFIX}/agents").mock(
            return_value=httpx.Response(409, json={"error": "Conflict"})
        )
        with pytest.raises(ConflictError):
            conn.post("/agents", {})

    @respx.mock
    def test_rate_limit_error(self, conn):
        respx.get(f"{BASE_URL}{API_PREFIX}/agents").mock(
            return_value=httpx.Response(429, json={"error": "Too many requests"}, headers={"Retry-After": "30"})
        )
        with pytest.raises(RateLimitError) as exc:
            conn.get("/agents")
        assert exc.value.retry_after == 30

    @respx.mock
    def test_server_error(self, conn):
        respx.get(f"{BASE_URL}{API_PREFIX}/agents").mock(
            return_value=httpx.Response(500, json={"error": "Internal server error"})
        )
        with pytest.raises(ServerError):
            conn.get("/agents")

    def test_https_enforcement(self):
        conn = Connection(api_key="test", base_url="http://evil.com", timeout=10)
        with pytest.raises(ValueError, match="Only HTTPS"):
            conn.get("/agents")

    def test_localhost_allowed(self):
        conn = Connection(api_key="test", base_url="http://localhost:3000", timeout=10)
        # Should not raise ValueError (will fail on connection, but that's OK)
        with respx.mock:
            respx.get("http://localhost:3000/api/v1/test").mock(
                return_value=httpx.Response(200, json={"ok": True})
            )
            result = conn.get("/test")
            assert result == {"ok": True}

    def test_repr_hides_api_key(self, conn):
        assert "[FILTERED]" in repr(conn)
        assert "wrb_test_xxx" not in repr(conn)

    @respx.mock
    def test_user_agent_header(self, conn):
        route = respx.get(f"{BASE_URL}{API_PREFIX}/agents").mock(
            return_value=httpx.Response(200, json={"data": []})
        )
        conn.get("/agents")
        assert "whatsrb-cloud-python/" in route.calls[0].request.headers["user-agent"]

    @respx.mock
    def test_bearer_auth_header(self, conn):
        route = respx.get(f"{BASE_URL}{API_PREFIX}/agents").mock(
            return_value=httpx.Response(200, json={"data": []})
        )
        conn.get("/agents")
        assert route.calls[0].request.headers["authorization"] == "Bearer wrb_test_xxx"
