import json
import pytest


def post_score(client, leaderboard, name, score, headers):
    return client.post(
        f"/{leaderboard}/add",
        data=json.dumps({"name": name, "score": score}),
        headers=headers,
    )


class TestAddScore:
    def test_returns_201_and_place(self, client, auth_headers):
        resp = post_score(client, "global", "Alice", 100, auth_headers)
        assert resp.status_code == 201
        data = resp.get_json()
        assert "place" in data
        assert data["place"] == 1

    def test_rank_reflects_current_standings(self, client, auth_headers):
        post_score(client, "global", "Alice", 200, auth_headers)
        post_score(client, "global", "Bob", 100, auth_headers)
        # Charlie scores 150 — one player (Alice) is higher, so rank 2
        resp = post_score(client, "global", "Charlie", 150, auth_headers)
        assert resp.get_json()["place"] == 2

    def test_every_insert_is_recorded(self, client, auth_headers):
        # All three inserts should succeed even for the same player
        for _ in range(3):
            resp = post_score(client, "global", "Alice", 100, auth_headers)
            assert resp.status_code == 201
        entries = client.get("/global/top/10").get_json()
        assert len(entries) == 3

    def test_requires_api_key(self, client):
        resp = client.post(
            "/global/add",
            data=json.dumps({"name": "Alice", "score": 100}),
            content_type="application/json",
        )
        assert resp.status_code == 401

    def test_wrong_api_key_rejected(self, client):
        resp = client.post(
            "/global/add",
            data=json.dumps({"name": "Alice", "score": 100}),
            content_type="application/json",
            headers={"X-API-Key": "wrong-key"},
        )
        assert resp.status_code == 401

    def test_invalid_leaderboard_returns_404(self, client, auth_headers):
        resp = post_score(client, "nonexistent", "Alice", 100, auth_headers)
        assert resp.status_code == 404

    def test_missing_name_returns_400(self, client, auth_headers):
        resp = client.post(
            "/global/add",
            data=json.dumps({"score": 100}),
            headers=auth_headers,
        )
        assert resp.status_code == 400

    def test_missing_score_returns_400(self, client, auth_headers):
        resp = client.post(
            "/global/add",
            data=json.dumps({"name": "Alice"}),
            headers=auth_headers,
        )
        assert resp.status_code == 400

    def test_non_integer_score_returns_400(self, client, auth_headers):
        resp = client.post(
            "/global/add",
            data=json.dumps({"name": "Alice", "score": "high"}),
            headers=auth_headers,
        )
        assert resp.status_code == 400

    def test_boolean_score_returns_400(self, client, auth_headers):
        # bool is a subclass of int in Python — we reject it explicitly
        resp = client.post(
            "/global/add",
            data=json.dumps({"name": "Alice", "score": True}),
            headers=auth_headers,
        )
        assert resp.status_code == 400

    def test_empty_name_returns_400(self, client, auth_headers):
        resp = client.post(
            "/global/add",
            data=json.dumps({"name": "   ", "score": 100}),
            headers=auth_headers,
        )
        assert resp.status_code == 400

    def test_non_json_body_returns_400(self, client, auth_headers):
        resp = client.post(
            "/global/add",
            data="not json",
            headers={**auth_headers, "Content-Type": "text/plain"},
        )
        assert resp.status_code == 400

    def test_all_leaderboards_accept_scores(self, client, auth_headers):
        for board in ["global", "weekly", "daily"]:
            resp = post_score(client, board, "Alice", 100, auth_headers)
            assert resp.status_code == 201
