from fastapi.testclient import TestClient

from grantflow.api.app import app

client = TestClient(app)


def _detail(response):
    detail = response.json()["detail"]
    assert isinstance(detail, dict)
    assert set(["code", "message", "retryable"]).issubset(detail)
    assert isinstance(detail["code"], str)
    assert isinstance(detail["message"], str)
    assert isinstance(detail["retryable"], bool)
    return detail


def test_protected_agent_bootstrap_errors_are_structured_for_agents(monkeypatch):
    monkeypatch.setenv("GRANTFLOW_API_KEY", "test-secret")

    response = client.post("/generate/preflight", json={"donor_id": "usaid"})

    assert response.status_code == 401
    detail = _detail(response)
    assert detail["code"] == "unauthorized"
    assert detail["retryable"] is False
    assert "X-API-Key" in detail["next_action"]


def test_invalid_request_id_error_is_structured_for_agents():
    response = client.post(
        "/generate",
        params={"request_id": "bad id with spaces"},
        json={
            "donor_id": "usaid",
            "input_context": {"project": "Public Health", "country": "Kenya"},
            "llm_mode": False,
            "hitl_enabled": False,
        },
    )

    assert response.status_code == 400
    detail = _detail(response)
    assert detail["code"] == "invalid_request_id"
    assert detail["request_id"] == "bad id with spaces"


def test_generate_startup_validation_errors_are_structured_for_agents():
    missing_donor = client.post(
        "/generate",
        json={
            "donor_id": " ",
            "input_context": {"project": "Public Health", "country": "Kenya"},
            "llm_mode": False,
            "hitl_enabled": False,
        },
    )
    assert missing_donor.status_code == 400
    assert _detail(missing_donor)["code"] == "missing_donor_id"

    invalid_webhook = client.post(
        "/generate",
        json={
            "donor_id": "usaid",
            "input_context": {"project": "Public Health", "country": "Kenya"},
            "llm_mode": False,
            "hitl_enabled": False,
            "webhook_url": "ftp://example.com/hook",
        },
    )
    assert invalid_webhook.status_code == 400
    assert _detail(invalid_webhook)["code"] == "invalid_webhook_url_scheme"

    unsupported_donor = client.post(
        "/generate",
        json={
            "donor_id": "not-a-donor",
            "input_context": {"project": "Public Health", "country": "Kenya"},
            "llm_mode": False,
            "hitl_enabled": False,
        },
    )
    assert unsupported_donor.status_code == 400
    detail = _detail(unsupported_donor)
    assert detail["code"] == "unsupported_donor_id"
    assert detail["donor_id"] == "not-a-donor"


def test_request_validation_errors_follow_agent_error_contract():
    # An unknown field triggers a Pydantic 422 — the most common mistake an agent
    # makes while learning the schema. It must still carry the agent error envelope
    # (structured next_action), not a raw FastAPI validation array.
    response = client.post(
        "/generate/preflight",
        json={"donor_id": "usaid", "unexpected_field": "x"},
    )

    assert response.status_code == 422
    detail = _detail(response)
    assert detail["code"] == "invalid_request"
    assert detail["retryable"] is False
    assert "next_action" in detail
    assert isinstance(detail["errors"], list) and detail["errors"]
    first_error = detail["errors"][0]
    assert {"loc", "msg", "type"}.issubset(first_error)
