from fastapi.testclient import TestClient

from app.db.session import init_db
from app.main import app
from tests.conftest import admin_headers


def _earn_bonus_coins(client: TestClient, target_child_id: int, needed: int = 12) -> None:
    earned = 0
    day = 20
    while earned < needed:
        dashboard = client.get(f"/api/dashboard?date=2026-06-{day:02d}").json()
        child_board = next(item for item in dashboard["children"] if item["child"]["id"] == target_child_id)
        for task in child_board["tasks"]["open"]:
            if task["kind"] == "bonus":
                client.post(f"/api/tasks/{task['id']}/complete", json={"idempotency_key": f"earn-{task['id']}"})
                earned += task["coin_value"]
        day += 1


def test_benefits_are_listed_with_affordability():
    init_db()
    with TestClient(app) as client:
        dashboard = client.get("/api/dashboard?date=2026-06-20").json()
        child_id = dashboard["children"][0]["child"]["id"]
        response = client.get(f"/api/benefits?child_id={child_id}")

    assert response.status_code == 200
    payload = response.json()
    assert len(payload["benefits"]) >= 3
    assert all("affordable" in item for item in payload["benefits"])
    assert any(item["title"] == "Extra Vorlesen" for item in payload["benefits"])


def test_redeem_benefit_reserves_coins_idempotently():
    init_db()
    with TestClient(app) as client:
        dashboard = client.get("/api/dashboard?date=2026-06-21").json()
        child_id = dashboard["children"][0]["child"]["id"]
        _earn_bonus_coins(client, child_id, needed=6)
        benefit = next(item for item in client.get(f"/api/benefits?child_id={child_id}").json()["benefits"] if item["price"] == 6)
        before = client.get(f"/api/coins/{child_id}/ledger").json()["summary"]

        first = client.post(f"/api/benefits/{benefit['id']}/redeem", json={"child_id": child_id, "idempotency_key": "redeem-1"})
        second = client.post(f"/api/benefits/{benefit['id']}/redeem", json={"child_id": child_id, "idempotency_key": "redeem-1"})
        ledger = client.get(f"/api/coins/{child_id}/ledger").json()

    assert first.status_code == 200
    assert first.json()["redemption"]["status"] == "reserved"
    assert second.status_code == 200
    assert second.json()["idempotent"] is True
    assert ledger["summary"] == {
        "posted": before["posted"],
        "reserved": before["reserved"] + 6,
        "available": before["available"] - 6,
    }
    assert len([tx for tx in ledger["transactions"] if tx["transaction_type"] == "reserve"]) == 1


def test_redeem_benefit_rejects_insufficient_coins():
    init_db()
    with TestClient(app) as client:
        dashboard = client.get("/api/dashboard?date=2026-06-24").json()
        child_id = dashboard["children"][0]["child"]["id"]
        benefit = next(item for item in client.get(f"/api/benefits?child_id={child_id}").json()["benefits"] if item["price"] == 12)
        before = client.get(f"/api/coins/{child_id}/ledger").json()["summary"]

        response = client.post(f"/api/benefits/{benefit['id']}/redeem", json={"child_id": child_id, "idempotency_key": "poor-1"})
        ledger = client.get(f"/api/coins/{child_id}/ledger").json()

    assert response.status_code == 409
    assert response.json()["detail"]["code"] == "insufficient_coins"
    assert ledger["summary"] == before


def test_fulfill_redemption_captures_reserved_coins_once():
    init_db()
    with TestClient(app) as client:
        dashboard = client.get("/api/dashboard?date=2026-06-22").json()
        child_id = dashboard["children"][0]["child"]["id"]
        _earn_bonus_coins(client, child_id, needed=6)
        benefit = next(item for item in client.get(f"/api/benefits?child_id={child_id}").json()["benefits"] if item["price"] == 6)
        before = client.get(f"/api/coins/{child_id}/ledger").json()["summary"]
        redemption_id = client.post(
            f"/api/benefits/{benefit['id']}/redeem",
            json={"child_id": child_id, "idempotency_key": "redeem-fulfill"},
        ).json()["redemption"]["id"]

        first = client.post(f"/api/admin/redemptions/{redemption_id}/fulfill", headers=admin_headers(client), json={"idempotency_key": "fulfill-1"})
        second = client.post(f"/api/admin/redemptions/{redemption_id}/fulfill", headers=admin_headers(client), json={"idempotency_key": "fulfill-1"})
        ledger = client.get(f"/api/coins/{child_id}/ledger").json()

    assert first.status_code == 200
    assert first.json()["redemption"]["status"] == "fulfilled"
    assert second.status_code == 200
    assert second.json()["idempotent"] is True
    assert ledger["summary"] == {
        "posted": before["posted"] - 6,
        "reserved": before["reserved"],
        "available": before["available"] - 6,
    }
    assert len([tx for tx in ledger["transactions"] if tx["transaction_type"] == "capture"]) == 1


def test_cancel_redemption_releases_reserved_coins():
    init_db()
    with TestClient(app) as client:
        dashboard = client.get("/api/dashboard?date=2026-06-23").json()
        child_id = dashboard["children"][0]["child"]["id"]
        _earn_bonus_coins(client, child_id, needed=6)
        benefit = next(item for item in client.get(f"/api/benefits?child_id={child_id}").json()["benefits"] if item["price"] == 6)
        before = client.get(f"/api/coins/{child_id}/ledger").json()["summary"]
        redemption_id = client.post(
            f"/api/benefits/{benefit['id']}/redeem",
            json={"child_id": child_id, "idempotency_key": "redeem-cancel"},
        ).json()["redemption"]["id"]

        cancel = client.post(f"/api/admin/redemptions/{redemption_id}/cancel", headers=admin_headers(client), json={"idempotency_key": "cancel-1"})
        ledger = client.get(f"/api/coins/{child_id}/ledger").json()

    assert cancel.status_code == 200
    assert cancel.json()["redemption"]["status"] == "cancelled"
    assert ledger["summary"] == before
    assert any(tx["transaction_type"] == "release" and tx["reserved_amount"] == -6 for tx in ledger["transactions"])
