"""The review game's autosave semantics: upsert ratings/notes, aspect votes,
draft visibility, and the final pick."""

from tests.conftest import admin_import_and_publish, as_user, import_package


def _bundle(client, project):
    resp = client.get(f"/api/projects/{project.id}/review")
    assert resp.status_code == 200
    return resp.json()


def test_bundle_empty_when_no_published_options(client, seed):
    as_user(client, "t_member")
    body = _bundle(client, seed.project)
    assert body["options"] == []


def test_patch_rating_saved_and_idempotent(client, seed):
    as_user(client, "t_admin")
    ids = admin_import_and_publish(client, seed.project, [{"slug": "alpha"}])
    option_id = ids["alpha"]

    as_user(client, "t_member")
    first = client.patch(
        f"/api/projects/{seed.project.id}/review/options/{option_id}", json={"rating": 2}
    )
    assert first.status_code == 200
    assert first.json() == {"option_id": option_id, "rating": 2, "note": ""}

    # Repeating the identical tap is idempotent.
    second = client.patch(
        f"/api/projects/{seed.project.id}/review/options/{option_id}", json={"rating": 2}
    )
    assert second.status_code == 200
    assert second.json()["rating"] == 2

    body = _bundle(client, seed.project)
    my_review = next(r for r in body["my_reviews"] if r["option_id"] == option_id)
    assert my_review["rating"] == 2


def test_rating_tap_does_not_blank_note(client, seed):
    as_user(client, "t_admin")
    ids = admin_import_and_publish(client, seed.project, [{"slug": "alpha"}])
    option_id = ids["alpha"]

    as_user(client, "t_member")
    note_resp = client.patch(
        f"/api/projects/{seed.project.id}/review/options/{option_id}", json={"note": "x"}
    )
    assert note_resp.status_code == 200

    rating_resp = client.patch(
        f"/api/projects/{seed.project.id}/review/options/{option_id}", json={"rating": 3}
    )
    assert rating_resp.status_code == 200

    body = _bundle(client, seed.project)
    my_review = next(r for r in body["my_reviews"] if r["option_id"] == option_id)
    assert my_review["rating"] == 3
    assert my_review["note"] == "x"


def test_rating_out_of_range_rejected(client, seed):
    as_user(client, "t_admin")
    ids = admin_import_and_publish(client, seed.project, [{"slug": "alpha"}])
    option_id = ids["alpha"]

    as_user(client, "t_member")
    too_high = client.patch(
        f"/api/projects/{seed.project.id}/review/options/{option_id}", json={"rating": 5}
    )
    assert too_high.status_code == 422

    too_low = client.patch(
        f"/api/projects/{seed.project.id}/review/options/{option_id}", json={"rating": -1}
    )
    assert too_low.status_code == 422


def test_aspect_vote_upsert_replace_and_clear(client, seed):
    as_user(client, "t_admin")
    ids = admin_import_and_publish(client, seed.project, [{"slug": "alpha"}])
    option_id = ids["alpha"]
    url = f"/api/projects/{seed.project.id}/review/options/{option_id}/aspects/colours"

    as_user(client, "t_member")

    up = client.put(url, json={"vote": 1})
    assert up.status_code == 200
    body = _bundle(client, seed.project)
    vote = next(v for v in body["my_aspect_votes"] if v["option_id"] == option_id)
    assert vote["vote"] == 1

    down = client.put(url, json={"vote": -1})
    assert down.status_code == 200
    body = _bundle(client, seed.project)
    vote = next(v for v in body["my_aspect_votes"] if v["option_id"] == option_id)
    assert vote["vote"] == -1

    cleared = client.put(url, json={"vote": 0})
    assert cleared.status_code == 200
    body = _bundle(client, seed.project)
    assert not any(v["option_id"] == option_id for v in body["my_aspect_votes"])


def test_aspect_vote_unknown_aspect_404(client, seed):
    as_user(client, "t_admin")
    ids = admin_import_and_publish(client, seed.project, [{"slug": "alpha"}])
    option_id = ids["alpha"]

    as_user(client, "t_member")
    resp = client.put(
        f"/api/projects/{seed.project.id}/review/options/{option_id}/aspects/nope",
        json={"vote": 1},
    )
    assert resp.status_code == 404
    assert resp.json()["detail"]["error_code"] == "UNKNOWN_ASPECT"


def test_aspect_vote_out_of_range_422(client, seed):
    as_user(client, "t_admin")
    ids = admin_import_and_publish(client, seed.project, [{"slug": "alpha"}])
    option_id = ids["alpha"]

    as_user(client, "t_member")
    resp = client.put(
        f"/api/projects/{seed.project.id}/review/options/{option_id}/aspects/colours",
        json={"vote": 2},
    )
    assert resp.status_code == 422


def test_rating_draft_option_member_404_admin_200(client, seed):
    as_user(client, "t_admin")
    import_resp = import_package(client, seed.project, [{"slug": "alpha"}])
    assert import_resp.status_code == 200
    listing = client.get(f"/api/projects/{seed.project.id}/options").json()
    option_id = next(o["id"] for o in listing if o["slug"] == "alpha")
    assert next(o["status"] for o in listing if o["slug"] == "alpha") == "draft"

    as_user(client, "t_member")
    member_resp = client.patch(
        f"/api/projects/{seed.project.id}/review/options/{option_id}", json={"rating": 2}
    )
    assert member_resp.status_code == 404
    assert member_resp.json()["detail"]["error_code"] == "OPTION_NOT_FOUND"

    as_user(client, "t_admin")
    admin_resp = client.patch(
        f"/api/projects/{seed.project.id}/review/options/{option_id}", json={"rating": 2}
    )
    assert admin_resp.status_code == 200


def test_final_pick_set_option(client, seed):
    as_user(client, "t_admin")
    ids = admin_import_and_publish(client, seed.project, [{"slug": "alpha"}])
    option_id = ids["alpha"]

    as_user(client, "t_member")
    resp = client.put(
        f"/api/projects/{seed.project.id}/review/final-pick", json={"option_id": option_id}
    )
    assert resp.status_code == 200
    assert resp.json()["option_id"] == option_id


def test_final_pick_closing_note_preserves_option_id(client, seed):
    as_user(client, "t_admin")
    ids = admin_import_and_publish(client, seed.project, [{"slug": "alpha"}])
    option_id = ids["alpha"]

    as_user(client, "t_member")
    first = client.put(
        f"/api/projects/{seed.project.id}/review/final-pick", json={"option_id": option_id}
    )
    assert first.status_code == 200

    second = client.put(
        f"/api/projects/{seed.project.id}/review/final-pick",
        json={"closing_note": "closing thoughts"},
    )
    assert second.status_code == 200
    body = second.json()
    assert body["option_id"] == option_id
    assert body["closing_note"] == "closing thoughts"


def test_final_pick_completed_persists_across_updates(client, seed):
    as_user(client, "t_admin")
    ids = admin_import_and_publish(client, seed.project, [{"slug": "alpha"}])
    option_id = ids["alpha"]

    as_user(client, "t_member")
    client.put(
        f"/api/projects/{seed.project.id}/review/final-pick", json={"option_id": option_id}
    )
    completed = client.put(
        f"/api/projects/{seed.project.id}/review/final-pick", json={"completed": True}
    )
    assert completed.status_code == 200
    completed_at = completed.json()["completed_at"]
    assert completed_at is not None

    later = client.put(
        f"/api/projects/{seed.project.id}/review/final-pick",
        json={"closing_note": "still thinking"},
    )
    assert later.status_code == 200
    assert later.json()["completed_at"] == completed_at
