# grantflow/tests/test_integration_review_workflow_sla_summary.py

from fastapi.testclient import TestClient


from grantflow.api import runtime_state

from grantflow.api.app import app

client = TestClient(app)


def test_status_review_workflow_sla_endpoint_aggregates_overdue_hotspots():
    job_id = "review-workflow-sla-job-1"
    runtime_state.JOB_STORE.set(
        job_id,
        {
            "status": "done",
            "state": {
                "critic_notes": {
                    "fatal_flaws": [
                        {
                            "finding_id": "finding-a",
                            "code": "TOC_SCHEMA_INVALID",
                            "severity": "high",
                            "section": "toc",
                            "status": "open",
                            "message": "ToC mismatch.",
                            "updated_at": "2026-02-27T07:00:00+00:00",
                            "due_at": "2026-02-27T08:00:00+00:00",
                            "sla_hours": 24,
                        },
                        {
                            "finding_id": "finding-b",
                            "code": "MEL_BASELINE_MISSING",
                            "severity": "medium",
                            "section": "logframe",
                            "status": "acknowledged",
                            "message": "Baseline missing.",
                            "acknowledged_at": "2026-02-27T09:30:00+00:00",
                            "due_at": "2026-02-27T12:00:00+00:00",
                            "sla_hours": 72,
                        },
                        {
                            "finding_id": "finding-c",
                            "code": "CITATION_GAP",
                            "severity": "low",
                            "section": "general",
                            "status": "resolved",
                            "message": "Citation linked.",
                            "resolved_at": "2026-02-27T10:30:00+00:00",
                            "due_at": "2026-02-28T10:00:00+00:00",
                            "sla_hours": 120,
                        },
                    ]
                }
            },
            "review_comments": [
                {
                    "comment_id": "comment-a",
                    "ts": "2026-02-27T06:30:00+00:00",
                    "section": "toc",
                    "status": "open",
                    "message": "Need stronger assumptions.",
                    "linked_finding_id": "finding-a",
                    "due_at": "2026-02-27T09:00:00+00:00",
                    "sla_hours": 24,
                },
                {
                    "comment_id": "comment-b",
                    "ts": "2026-02-27T10:20:00+00:00",
                    "section": "logframe",
                    "status": "open",
                    "message": "Indicator wording update pending.",
                    "linked_finding_id": "finding-b",
                    "due_at": "2026-02-27T12:30:00+00:00",
                    "sla_hours": 72,
                },
                {
                    "comment_id": "comment-c",
                    "ts": "2026-02-27T10:35:00+00:00",
                    "section": "general",
                    "status": "resolved",
                    "message": "Resolved by reviewer.",
                    "linked_finding_id": "finding-c",
                    "due_at": "2026-02-27T13:00:00+00:00",
                    "sla_hours": 72,
                },
            ],
            "job_events": [
                {
                    "event_id": "rwf-sla-1",
                    "ts": "2026-02-27T07:00:00+00:00",
                    "type": "critic_finding_status_changed",
                    "finding_id": "finding-a",
                    "status": "open",
                    "section": "toc",
                    "severity": "high",
                },
                {
                    "event_id": "rwf-sla-2",
                    "ts": "2026-02-27T09:30:00+00:00",
                    "type": "critic_finding_status_changed",
                    "finding_id": "finding-b",
                    "status": "acknowledged",
                    "section": "logframe",
                    "severity": "medium",
                },
                {
                    "event_id": "rwf-sla-3",
                    "ts": "2026-02-27T10:40:00+00:00",
                    "type": "review_comment_status_changed",
                    "comment_id": "comment-c",
                    "status": "resolved",
                    "section": "general",
                },
            ],
        },
    )

    resp = client.get(f"/status/{job_id}/review/workflow/sla", params={"overdue_after_hours": 2})
    assert resp.status_code == 200
    body = resp.json()
    assert body["job_id"] == job_id
    assert body["status"] == "done"
    assert body["filters"] == {"overdue_after_hours": 2}
    assert body["overdue_after_hours"] == 2
    assert body["finding_total"] == 3
    assert body["comment_total"] == 3
    assert body["unresolved_finding_count"] == 2
    assert body["unresolved_comment_count"] == 2
    assert body["unresolved_total"] == 4
    assert body["overdue_finding_count"] == 1
    assert body["overdue_comment_count"] == 1
    assert body["overdue_total"] == 2
    assert body["breach_rate"] == 0.5
    assert body["overdue_by_section"]["toc"] == 2
    assert body["overdue_by_severity"]["high"] == 2
    assert body["oldest_overdue"]["kind"] == "finding"
    assert body["oldest_overdue"]["id"] == "finding-a"
    assert len(body["top_overdue"]) == 2
    assert body["top_overdue"][0]["id"] == "finding-a"
    assert body["top_overdue"][1]["id"] == "comment-a"

    section_filtered = client.get(
        f"/status/{job_id}/review/workflow/sla",
        params={"finding_section": "logframe", "overdue_after_hours": 2},
    )
    assert section_filtered.status_code == 200
    section_payload = section_filtered.json()
    assert section_payload["filters"]["finding_section"] == "logframe"
    assert section_payload["filters"]["overdue_after_hours"] == 2
    assert section_payload["finding_total"] == 1
    assert section_payload["comment_total"] == 1
    assert section_payload["overdue_total"] == 0
    assert section_payload["top_overdue"] == []

    code_filtered = client.get(
        f"/status/{job_id}/review/workflow/sla",
        params={"finding_code": "TOC_SCHEMA_INVALID", "overdue_after_hours": 2},
    )
    assert code_filtered.status_code == 200
    code_payload = code_filtered.json()
    assert code_payload["filters"]["finding_code"] == "TOC_SCHEMA_INVALID"
    assert code_payload["filters"]["overdue_after_hours"] == 2
    assert code_payload["finding_total"] == 1
    assert code_payload["comment_total"] == 1
    assert code_payload["overdue_total"] == 2
    assert {row["id"] for row in code_payload["top_overdue"]} == {"finding-a", "comment-a"}

    workflow_filtered = client.get(
        f"/status/{job_id}/review/workflow/sla",
        params={"workflow_state": "pending", "overdue_after_hours": 2},
    )
    assert workflow_filtered.status_code == 200
    workflow_payload = workflow_filtered.json()
    assert workflow_payload["filters"]["workflow_state"] == "pending"
    assert workflow_payload["filters"]["overdue_after_hours"] == 2
    assert workflow_payload["overdue_total"] == 0
    assert workflow_payload["finding_total"] == 1
    assert workflow_payload["comment_total"] == 1

    invalid_state = client.get(
        f"/status/{job_id}/review/workflow/sla",
        params={"workflow_state": "inbox", "overdue_after_hours": 2},
    )
    assert invalid_state.status_code == 400
    invalid_section = client.get(
        f"/status/{job_id}/review/workflow/sla",
        params={"finding_section": "budget", "overdue_after_hours": 2},
    )
    assert invalid_section.status_code == 400
