# grantflow/tests/test_integration_review_workflow_sla_profile_recompute.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_profile_endpoint_returns_saved_or_default_profile():
    saved_job_id = "review-workflow-sla-profile-saved-job-1"
    runtime_state.JOB_STORE.set(
        saved_job_id,
        {
            "status": "done",
            "client_metadata": {
                "sla_profile": {
                    "finding_sla_hours": {"high": 11, "medium": 33, "low": 77},
                    "default_comment_sla_hours": 42,
                    "updated_at": "2026-02-27T12:00:00+00:00",
                    "updated_by": "qa-user",
                }
            },
            "state": {"critic_notes": {"fatal_flaws": []}},
            "review_comments": [],
        },
    )
    saved_resp = client.get(f"/status/{saved_job_id}/review/workflow/sla/profile")
    assert saved_resp.status_code == 200
    saved_body = saved_resp.json()
    assert saved_body["source"] == "saved"
    assert saved_body["saved_profile_available"] is True
    assert saved_body["saved_profile_valid"] is True
    assert saved_body["finding_sla_hours"] == {"high": 11, "medium": 33, "low": 77}
    assert int(saved_body["default_comment_sla_hours"]) == 42
    assert saved_body["saved_profile_updated_at"] == "2026-02-27T12:00:00+00:00"
    assert saved_body["saved_profile_updated_by"] == "qa-user"

    default_job_id = "review-workflow-sla-profile-default-job-1"
    runtime_state.JOB_STORE.set(
        default_job_id,
        {
            "status": "done",
            "state": {"critic_notes": {"fatal_flaws": []}},
            "review_comments": [],
        },
    )
    default_resp = client.get(f"/status/{default_job_id}/review/workflow/sla/profile")
    assert default_resp.status_code == 200
    default_body = default_resp.json()
    assert default_body["source"] == "default"
    assert default_body["saved_profile_available"] is False
    assert default_body["saved_profile_valid"] is True
    assert default_body["finding_sla_hours"] == {"high": 24, "medium": 72, "low": 120}
    assert int(default_body["default_comment_sla_hours"]) == 72


def test_status_review_workflow_sla_recompute_rewrites_due_dates_and_emits_event():
    job_id = "review-workflow-sla-recompute-job-1"
    runtime_state.JOB_STORE.set(
        job_id,
        {
            "status": "done",
            "state": {
                "critic_notes": {
                    "fatal_flaws": [
                        {
                            "finding_id": "finding-r1",
                            "code": "TOC_SCHEMA_INVALID",
                            "severity": "high",
                            "section": "toc",
                            "status": "open",
                            "message": "ToC mismatch.",
                            "updated_at": "2026-02-27T08:00:00+00:00",
                            "due_at": "2000-01-01T00:00:00+00:00",
                            "sla_hours": 24,
                        }
                    ]
                }
            },
            "review_comments": [
                {
                    "comment_id": "comment-r1",
                    "ts": "2026-02-27T08:30:00+00:00",
                    "section": "toc",
                    "status": "open",
                    "message": "Need stronger assumptions.",
                    "linked_finding_id": "finding-r1",
                    "due_at": "2000-01-01T00:00:00+00:00",
                    "sla_hours": 24,
                },
                {
                    "comment_id": "comment-r2",
                    "ts": "2026-02-27T09:00:00+00:00",
                    "section": "general",
                    "status": "open",
                    "message": "General review queue.",
                    "due_at": "2000-01-01T00:00:00+00:00",
                    "sla_hours": 72,
                },
            ],
            "job_events": [],
        },
    )

    resp = client.post(
        f"/status/{job_id}/review/workflow/sla/recompute",
        json={
            "finding_sla_hours": {"high": 12, "medium": 48, "low": 96},
            "default_comment_sla_hours": 36,
        },
        headers={"X-Reviewer": "qa-recompute"},
    )
    assert resp.status_code == 200
    body = resp.json()
    assert body["job_id"] == job_id
    assert body["status"] == "done"
    assert body["actor"] == "qa-recompute"
    assert body["use_saved_profile"] is False
    assert body["applied_finding_sla_hours"] == {"high": 12, "medium": 48, "low": 96}
    assert body["applied_default_comment_sla_hours"] == 36
    assert body["finding_checked_count"] == 1
    assert body["comment_checked_count"] == 2
    assert body["finding_updated_count"] == 1
    assert body["comment_updated_count"] == 2
    assert body["total_updated_count"] == 3
    assert body["sla"]["job_id"] == job_id

    critic_resp = client.get(f"/status/{job_id}/critic")
    assert critic_resp.status_code == 200
    critic_finding = critic_resp.json()["fatal_flaws"][0]
    assert critic_finding["due_at"] == "2026-02-27T20:00:00+00:00"
    assert int(critic_finding.get("sla_hours") or 0) == 12

    comments_resp = client.get(f"/status/{job_id}/comments")
    assert comments_resp.status_code == 200
    comments_by_id = {row["comment_id"]: row for row in comments_resp.json()["comments"]}
    assert comments_by_id["comment-r1"]["due_at"] == "2026-02-27T20:30:00+00:00"
    assert int(comments_by_id["comment-r1"].get("sla_hours") or 0) == 12
    assert comments_by_id["comment-r2"]["due_at"] == "2026-02-28T21:00:00+00:00"
    assert int(comments_by_id["comment-r2"].get("sla_hours") or 0) == 36

    events_resp = client.get(f"/status/{job_id}/events")
    assert events_resp.status_code == 200
    event_rows = [e for e in events_resp.json()["events"] if e.get("type") == "review_workflow_sla_recomputed"]
    assert event_rows
    last = event_rows[-1]
    assert last["actor"] == "qa-recompute"
    assert int(last["total_updated_count"]) == 3
    assert last["use_saved_profile"] is False
    assert last["applied_finding_sla_hours"] == {"high": 12, "medium": 48, "low": 96}
    assert int(last["applied_default_comment_sla_hours"]) == 36

    stored_job = runtime_state.JOB_STORE.get(job_id)
    assert isinstance(stored_job, dict)
    stored_meta = stored_job.get("client_metadata")
    assert isinstance(stored_meta, dict)
    saved_profile = stored_meta.get("sla_profile")
    assert isinstance(saved_profile, dict)
    assert saved_profile["finding_sla_hours"] == {"high": 12, "medium": 48, "low": 96}
    assert int(saved_profile["default_comment_sla_hours"]) == 36


def test_status_review_workflow_sla_recompute_can_use_saved_profile():
    job_id = "review-workflow-sla-recompute-use-saved-job-1"
    runtime_state.JOB_STORE.set(
        job_id,
        {
            "status": "done",
            "state": {
                "critic_notes": {
                    "fatal_flaws": [
                        {
                            "finding_id": "finding-saved-1",
                            "code": "TOC_SCHEMA_INVALID",
                            "severity": "high",
                            "section": "toc",
                            "status": "open",
                            "message": "ToC mismatch.",
                            "updated_at": "2026-02-27T08:00:00+00:00",
                            "due_at": "2000-01-01T00:00:00+00:00",
                            "sla_hours": 24,
                        }
                    ]
                }
            },
            "review_comments": [
                {
                    "comment_id": "comment-saved-1",
                    "ts": "2026-02-27T09:00:00+00:00",
                    "section": "general",
                    "status": "open",
                    "message": "General queue.",
                    "due_at": "2000-01-01T00:00:00+00:00",
                    "sla_hours": 72,
                }
            ],
            "client_metadata": {
                "sla_profile": {
                    "finding_sla_hours": {"high": 10, "medium": 20, "low": 30},
                    "default_comment_sla_hours": 16,
                }
            },
            "job_events": [],
        },
    )

    resp = client.post(
        f"/status/{job_id}/review/workflow/sla/recompute",
        json={"use_saved_profile": True},
        headers={"X-Reviewer": "qa-saved"},
    )
    assert resp.status_code == 200
    body = resp.json()
    assert body["use_saved_profile"] is True
    assert body["applied_finding_sla_hours"] == {"high": 10, "medium": 20, "low": 30}
    assert int(body["applied_default_comment_sla_hours"]) == 16

    critic_resp = client.get(f"/status/{job_id}/critic")
    assert critic_resp.status_code == 200
    critic_finding = critic_resp.json()["fatal_flaws"][0]
    assert critic_finding["due_at"] == "2026-02-27T18:00:00+00:00"
    assert int(critic_finding.get("sla_hours") or 0) == 10

    comments_resp = client.get(f"/status/{job_id}/comments")
    assert comments_resp.status_code == 200
    comment = comments_resp.json()["comments"][0]
    assert comment["due_at"] == "2026-02-28T01:00:00+00:00"
    assert int(comment.get("sla_hours") or 0) == 16


def test_status_review_workflow_sla_recompute_rejects_invalid_profile():
    job_id = "review-workflow-sla-recompute-invalid-job-1"
    runtime_state.JOB_STORE.set(
        job_id,
        {
            "status": "done",
            "state": {"critic_notes": {"fatal_flaws": []}},
            "review_comments": [],
        },
    )

    bad_key = client.post(
        f"/status/{job_id}/review/workflow/sla/recompute",
        json={"finding_sla_hours": {"critical": 6}},
    )
    assert bad_key.status_code == 400
    assert "Unsupported SLA severity key" in bad_key.json()["detail"]

    bad_value = client.post(
        f"/status/{job_id}/review/workflow/sla/recompute",
        json={"default_comment_sla_hours": 0},
    )
    assert bad_value.status_code == 400
    assert bad_value.json()["detail"]["code"] == "invalid_parameter"
