# grantflow/tests/test_integration_portfolio_metrics.py

from fastapi.testclient import TestClient


from grantflow.api import runtime_state

from grantflow.api.app import app

client = TestClient(app)


def test_portfolio_metrics_endpoint_aggregates_jobs_and_filters():
    runtime_state.JOB_STORE.set(
        "portfolio-job-1",
        {
            "status": "done",
            "hitl_enabled": True,
            "generate_preflight": {"warning_level": "medium", "risk_level": "medium"},
            "state": {
                "donor_id": "usaid",
                "critic_notes": {
                    "rule_checks": [{"code": "TOC_TEXT_COMPLETENESS", "status": "fail"}],
                },
                "citations": [
                    {"citation_type": "fallback_namespace"},
                    {"citation_type": "fallback_namespace"},
                    {"citation_type": "fallback_namespace"},
                ],
            },
            "job_events": [
                {
                    "event_id": "a1",
                    "ts": "2026-02-24T10:00:00+00:00",
                    "type": "status_changed",
                    "to_status": "accepted",
                    "status": "accepted",
                },
                {
                    "event_id": "a2",
                    "ts": "2026-02-24T10:00:05+00:00",
                    "type": "status_changed",
                    "to_status": "running",
                    "status": "running",
                },
                {
                    "event_id": "a3",
                    "ts": "2026-02-24T10:00:20+00:00",
                    "type": "status_changed",
                    "to_status": "pending_hitl",
                    "status": "pending_hitl",
                },
                {"event_id": "a4", "ts": "2026-02-24T10:01:00+00:00", "type": "resume_requested", "status": "accepted"},
                {
                    "event_id": "a5",
                    "ts": "2026-02-24T10:01:02+00:00",
                    "type": "status_changed",
                    "to_status": "accepted",
                    "status": "accepted",
                },
                {
                    "event_id": "a6",
                    "ts": "2026-02-24T10:01:03+00:00",
                    "type": "status_changed",
                    "to_status": "running",
                    "status": "running",
                },
                {
                    "event_id": "a7",
                    "ts": "2026-02-24T10:02:00+00:00",
                    "type": "status_changed",
                    "to_status": "done",
                    "status": "done",
                },
            ],
        },
    )
    runtime_state.JOB_STORE.set(
        "portfolio-job-2",
        {
            "status": "error",
            "hitl_enabled": False,
            "generate_preflight": {"warning_level": "high", "risk_level": "high"},
            "state": {
                "donor_id": "eu",
                "critic_notes": {
                    "rule_checks": [{"code": "TOC_TEXT_COMPLETENESS", "status": "pass"}],
                },
                "citations": [
                    {"citation_type": "rag_claim_support"},
                    {"citation_type": "rag_claim_support"},
                    {"citation_type": "rag_support"},
                    {"citation_type": "rag_support"},
                ],
            },
            "job_events": [
                {
                    "event_id": "b1",
                    "ts": "2026-02-24T11:00:00+00:00",
                    "type": "status_changed",
                    "to_status": "accepted",
                    "status": "accepted",
                },
                {
                    "event_id": "b2",
                    "ts": "2026-02-24T11:00:03+00:00",
                    "type": "status_changed",
                    "to_status": "running",
                    "status": "running",
                },
                {
                    "event_id": "b3",
                    "ts": "2026-02-24T11:00:30+00:00",
                    "type": "status_changed",
                    "to_status": "error",
                    "status": "error",
                },
            ],
        },
    )

    response = client.get("/portfolio/metrics")
    assert response.status_code == 200
    body = response.json()
    assert body["job_count"] >= 2
    assert body["status_counts"]["done"] >= 1
    assert body["status_counts"]["error"] >= 1
    assert body["donor_counts"]["usaid"] >= 1
    assert body["donor_counts"]["eu"] >= 1
    assert body["warning_level_counts"]["medium"] >= 1
    assert body["warning_level_counts"]["high"] >= 1
    assert body["warning_level_job_counts"]["high"] >= 1
    assert body["warning_level_job_counts"]["medium"] >= 1
    assert "low" in body["warning_level_job_counts"]
    assert "none" in body["warning_level_job_counts"]
    assert sum(int(v or 0) for v in body["warning_level_job_counts"].values()) == body["job_count"]
    assert body["warning_level_job_rates"]["high"] is not None
    assert body["warning_level_job_rates"]["medium"] is not None
    assert body["warning_level_job_rates"]["low"] is not None
    assert body["warning_level_job_rates"]["none"] is not None
    assert body["grounding_risk_counts"]["high"] >= 1
    assert body["grounding_risk_counts"]["low"] >= 1
    assert body["grounding_risk_job_counts"]["high"] >= 1
    assert body["grounding_risk_job_counts"]["low"] >= 1
    assert body["grounding_risk_job_counts"]["unknown"] >= 0
    assert sum(int(v or 0) for v in body["grounding_risk_job_counts"].values()) == body["job_count"]
    assert body["grounding_risk_job_rates"]["high"] is not None
    assert body["grounding_risk_job_rates"]["medium"] is not None
    assert body["grounding_risk_job_rates"]["low"] is not None
    assert body["grounding_risk_job_rates"]["unknown"] is not None
    assert body["mel_risk_counts"].get("high", 0) >= 0
    assert body["mel_risk_counts"].get("medium", 0) >= 0
    assert body["mel_risk_counts"].get("low", 0) >= 0
    assert body["mel_risk_counts"].get("unknown", 0) >= 0
    assert sum(int(v or 0) for v in body["mel_risk_job_counts"].values()) == body["job_count"]
    assert body["mel_risk_job_rates"]["high"] is not None
    assert body["mel_risk_job_rates"]["medium"] is not None
    assert body["mel_risk_job_rates"]["low"] is not None
    assert body["mel_risk_job_rates"]["unknown"] is not None
    assert body["terminal_job_count"] >= 2
    assert body["hitl_job_count"] >= 1
    assert body["total_pause_count"] >= 1
    assert body["total_resume_count"] >= 1
    assert body["avg_time_to_terminal_seconds"] is not None
    assert "review_readiness_summary" in body
    assert body["review_readiness_summary"]["needs_revision_job_count"] >= 0
    assert body["review_readiness_summary"]["open_critic_findings"] >= 0
    assert body["review_readiness_summary"]["open_review_comments"] >= 0
    assert "open_critic_findings_per_job_avg" in body["review_readiness_summary"]
    assert "triage_summary" in body["review_readiness_summary"]
    assert isinstance(body["review_readiness_summary"]["triage_summary"].get("top_reviewer_actions"), list)

    filtered = client.get("/portfolio/metrics", params={"donor_id": "usaid", "status": "done", "hitl_enabled": "true"})
    assert filtered.status_code == 200
    filtered_body = filtered.json()
    assert filtered_body["filters"]["donor_id"] == "usaid"
    assert filtered_body["filters"]["status"] == "done"
    assert filtered_body["filters"]["hitl_enabled"] is True
    assert filtered_body["filters"].get("toc_text_risk_level") is None
    assert filtered_body["filters"].get("mel_risk_level") is None
    assert filtered_body["job_count"] >= 1
    assert "error" not in filtered_body["status_counts"]
    assert filtered_body["warning_level_counts"]["medium"] >= 1
    assert filtered_body["warning_level_job_counts"]["medium"] >= 1

    warning_filtered = client.get("/portfolio/metrics", params={"warning_level": "high"})
    assert warning_filtered.status_code == 200
    warning_filtered_body = warning_filtered.json()
    assert warning_filtered_body["filters"]["warning_level"] == "high"
    assert warning_filtered_body["job_count"] >= 1
    assert warning_filtered_body["warning_level_counts"] == {"high": warning_filtered_body["job_count"]}
    assert warning_filtered_body["warning_level_job_counts"]["high"] == warning_filtered_body["job_count"]
    assert warning_filtered_body["warning_level_job_counts"]["medium"] == 0

    grounding_filtered = client.get("/portfolio/metrics", params={"grounding_risk_level": "high"})
    assert grounding_filtered.status_code == 200
    grounding_filtered_body = grounding_filtered.json()
    assert grounding_filtered_body["filters"]["grounding_risk_level"] == "high"
    assert grounding_filtered_body["job_count"] >= 1
    assert grounding_filtered_body["grounding_risk_counts"] == {"high": grounding_filtered_body["job_count"]}
    assert grounding_filtered_body["grounding_risk_job_counts"]["high"] == grounding_filtered_body["job_count"]
    assert grounding_filtered_body["grounding_risk_job_counts"]["low"] == 0

    toc_text_filtered = client.get("/portfolio/metrics", params={"toc_text_risk_level": "high"})
    assert toc_text_filtered.status_code == 200
    toc_text_filtered_body = toc_text_filtered.json()
    assert toc_text_filtered_body["filters"]["toc_text_risk_level"] == "high"
    assert toc_text_filtered_body["job_count"] >= 1
    assert toc_text_filtered_body["status_counts"].get("done", 0) >= 1
    assert toc_text_filtered_body["status_counts"].get("error", 0) == 0

    mel_level = next(
        (level for level, count in (body.get("mel_risk_job_counts") or {}).items() if int(count or 0) > 0),
        "unknown",
    )
    mel_filtered = client.get("/portfolio/metrics", params={"mel_risk_level": mel_level})
    assert mel_filtered.status_code == 200
    mel_filtered_body = mel_filtered.json()
    assert mel_filtered_body["filters"]["mel_risk_level"] == mel_level
    assert mel_filtered_body["job_count"] >= 1
    assert mel_filtered_body["mel_risk_job_counts"][mel_level] == mel_filtered_body["job_count"]
