# grantflow/tests/test_integration_export_payload_contract.py
"""
Contract tests for GET /status/{job_id}/export-payload.

Covers:
- Response shape: top-level keys, payload sub-keys
- 404 for unknown job
- Auth guard when API key is configured
- strategy / donor_strategy redacted from payload.state
- submission_package_readiness and export_contract present
- Tenant read-access guard
"""

from fastapi.testclient import TestClient

from grantflow.api.app import app
from grantflow.tests.integration_helpers import wait_for_terminal_status

client = TestClient(app)

_EXPORT_PAYLOAD_TOP_KEYS = {"job_id", "status", "payload"}
_EXPORT_PAYLOAD_SUB_KEYS = {
    "state",
    "quality_summary",
    "critic_findings",
    "review_comments",
    "readiness",
    "review_readiness_summary",
    "architect_citation_signals",
    "submission_package_readiness",
    "export_contract",
}

_GENERATE_BODY = {
    "donor_id": "usaid",
    "input_context": {"project": "Health Systems", "country": "Kenya"},
    "llm_mode": False,
    "hitl_enabled": False,
}


def _run_job() -> str:
    res = client.post("/generate", json=_GENERATE_BODY)
    assert res.status_code == 200
    job_id = res.json()["job_id"]
    wait_for_terminal_status(client, job_id)
    return job_id


def test_export_payload_top_level_shape():
    job_id = _run_job()
    res = client.get(f"/status/{job_id}/export-payload")
    assert res.status_code == 200
    body = res.json()
    assert _EXPORT_PAYLOAD_TOP_KEYS <= body.keys()
    assert body["job_id"] == job_id
    assert body["status"] in {"done", "error", "canceled"}


def test_export_payload_sub_keys_present():
    job_id = _run_job()
    res = client.get(f"/status/{job_id}/export-payload")
    assert res.status_code == 200
    payload = res.json().get("payload") or {}
    missing = _EXPORT_PAYLOAD_SUB_KEYS - payload.keys()
    assert not missing, f"export-payload missing sub-keys: {missing}"


def test_export_payload_redacts_strategy_from_state():
    job_id = _run_job()
    res = client.get(f"/status/{job_id}/export-payload")
    assert res.status_code == 200
    state = (res.json().get("payload") or {}).get("state") or {}
    assert "strategy" not in state, "strategy must be redacted from public export payload"
    assert "donor_strategy" not in state, "donor_strategy must be redacted from public export payload"


def test_export_payload_submission_package_readiness_is_dict():
    job_id = _run_job()
    res = client.get(f"/status/{job_id}/export-payload")
    assert res.status_code == 200
    readiness = (res.json().get("payload") or {}).get("submission_package_readiness")
    assert isinstance(readiness, dict), "submission_package_readiness must be a dict"


def test_export_payload_export_contract_is_dict():
    job_id = _run_job()
    res = client.get(f"/status/{job_id}/export-payload")
    assert res.status_code == 200
    export_contract = (res.json().get("payload") or {}).get("export_contract")
    assert isinstance(export_contract, dict), "export_contract must be a dict"


def test_export_payload_404_for_unknown_job():
    res = client.get("/status/nonexistent-export-job-xyz/export-payload")
    assert res.status_code == 404


def test_export_payload_requires_api_key_when_configured(monkeypatch):
    job_id = _run_job()
    monkeypatch.setenv("GRANTFLOW_API_KEY", "test-secret")
    monkeypatch.setenv("GRANTFLOW_REQUIRE_AUTH_FOR_READS", "true")

    res = client.get(f"/status/{job_id}/export-payload")
    assert res.status_code == 401

    res = client.get(f"/status/{job_id}/export-payload", headers={"X-API-Key": "test-secret"})
    assert res.status_code == 200


def test_export_payload_tenant_access_blocked_for_wrong_tenant(monkeypatch):
    """A job created for tenant_a must not be readable by tenant_b."""
    monkeypatch.setenv("GRANTFLOW_API_KEY", "test-secret")

    body = {**_GENERATE_BODY, "tenant_id": "tenant_a_export"}
    res = client.post("/generate", json=body, headers={"X-API-Key": "test-secret"})
    assert res.status_code == 200
    job_id = res.json()["job_id"]
    # poll before enabling tenant authz so status endpoint is unguarded
    wait_for_terminal_status(client, job_id)

    monkeypatch.setenv("GRANTFLOW_TENANT_AUTHZ_ENABLED", "true")

    res = client.get(
        f"/status/{job_id}/export-payload",
        headers={"X-API-Key": "test-secret", "X-Tenant-ID": "tenant_b_export"},
    )
    assert res.status_code == 403
