from fastapi.testclient import TestClient

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

client = TestClient(app)


def test_status_comments_create_request_id_is_idempotent():
    response = client.post(
        "/generate",
        json={
            "donor_id": "usaid",
            "input_context": {"project": "Education", "country": "Kenya"},
            "llm_mode": False,
            "hitl_enabled": False,
        },
    )
    assert response.status_code == 200
    job_id = response.json()["job_id"]
    wait_for_terminal_status(client, job_id)

    first = client.post(
        f"/status/{job_id}/comments",
        json={
            "section": "toc",
            "message": "Clarify outcome assumptions.",
            "author": "reviewer-idem",
            "request_id": "rid-comment-create-1",
        },
    )
    assert first.status_code == 200
    first_body = first.json()
    assert first_body["request_id"] == "rid-comment-create-1"
    assert first_body.get("idempotent_replay") is not True

    replay = client.post(
        f"/status/{job_id}/comments",
        json={
            "section": "toc",
            "message": "Clarify outcome assumptions.",
            "author": "reviewer-idem",
            "request_id": "rid-comment-create-1",
        },
    )
    assert replay.status_code == 200
    replay_body = replay.json()
    assert replay_body["request_id"] == "rid-comment-create-1"
    assert replay_body["idempotent_replay"] is True
    assert replay_body["comment_id"] == first_body["comment_id"]

    mismatch = client.post(
        f"/status/{job_id}/comments",
        json={
            "section": "toc",
            "message": "Different payload for same request id.",
            "author": "reviewer-idem",
            "request_id": "rid-comment-create-1",
        },
    )
    assert mismatch.status_code == 409
    mismatch_detail = mismatch.json().get("detail") or {}
    assert mismatch_detail.get("reason") == "request_id_reused_with_different_payload"

    job = runtime_state.JOB_STORE.get(job_id) or {}
    events = job.get("job_events") if isinstance(job.get("job_events"), list) else []
    comment_events = [e for e in events if isinstance(e, dict) and e.get("type") == "review_comment_added"]
    assert len(comment_events) == 1
    assert comment_events[0].get("request_id") == "rid-comment-create-1"


def test_status_comments_status_request_id_is_idempotent():
    response = client.post(
        "/generate",
        json={
            "donor_id": "usaid",
            "input_context": {"project": "Governance", "country": "Jordan"},
            "llm_mode": False,
            "hitl_enabled": False,
        },
    )
    assert response.status_code == 200
    job_id = response.json()["job_id"]
    wait_for_terminal_status(client, job_id)

    create_resp = client.post(
        f"/status/{job_id}/comments",
        json={"section": "general", "message": "Needs tighter risks section."},
    )
    assert create_resp.status_code == 200
    comment_id = create_resp.json()["comment_id"]

    first = client.post(
        f"/status/{job_id}/comments/{comment_id}/resolve",
        params={"request_id": "rid-comment-status-1"},
    )
    assert first.status_code == 200
    first_body = first.json()
    assert first_body["status"] == "resolved"
    assert first_body["request_id"] == "rid-comment-status-1"
    assert first_body.get("idempotent_replay") is not True

    replay = client.post(
        f"/status/{job_id}/comments/{comment_id}/resolve",
        params={"request_id": "rid-comment-status-1"},
    )
    assert replay.status_code == 200
    replay_body = replay.json()
    assert replay_body["status"] == "resolved"
    assert replay_body["request_id"] == "rid-comment-status-1"
    assert replay_body["idempotent_replay"] is True

    mismatch = client.post(
        f"/status/{job_id}/comments/{comment_id}/reopen",
        params={"request_id": "rid-comment-status-1"},
    )
    assert mismatch.status_code == 409
    mismatch_detail = mismatch.json().get("detail") or {}
    assert mismatch_detail.get("reason") == "request_id_reused_with_different_payload"

    job = runtime_state.JOB_STORE.get(job_id) or {}
    events = job.get("job_events") if isinstance(job.get("job_events"), list) else []
    status_events = [e for e in events if isinstance(e, dict) and e.get("type") == "review_comment_status_changed"]
    matching = [e for e in status_events if e.get("comment_id") == comment_id]
    assert len(matching) == 1
    assert matching[0].get("request_id") == "rid-comment-status-1"
