"""Webhook Stripe + règle « seules les commandes payées comptent »."""
import uuid

from conftest import DEMO_CODE, SCHOOL, OTHER_CODE, OTHER_SCHOOL, insert_order, item


def _event(kind: str, pi: str, amount: int | None = None) -> dict:
    obj = {"id": pi}
    if amount is not None:
        obj["amount_received"] = amount
        obj["amount"] = amount
    return {"type": kind, "data": {"object": obj}}


def _new_order(**kw):
    oid = str(uuid.uuid4())
    pi = "pi_" + uuid.uuid4().hex[:12]
    insert_order(oid, kw.pop("code", DEMO_CODE), kw.pop("school", SCHOOL), kw.pop("items", [item()]),
                 payment_status=kw.pop("payment_status", "unpaid"), pi_id=pi, **kw)
    return oid, pi


async def _order(client, oid, code=DEMO_CODE):
    orders = (await client.get(f"/api/portal/orders?code={code}")).json()["orders"]
    return next(o for o in orders if o["id"] == oid)


async def test_webhook_marks_order_paid_and_is_idempotent(client):
    oid, pi = _new_order()
    for _ in range(2):
        r = await client.post("/api/stripe/webhook", json=_event("payment_intent.succeeded", pi, 900))
        assert r.status_code == 200
    assert (await _order(client, oid))["payment_status"] == "paid"


async def test_webhook_amount_mismatch_does_not_mark_paid(client):
    oid, pi = _new_order()
    await client.post("/api/stripe/webhook", json=_event("payment_intent.succeeded", pi, 1))
    assert (await _order(client, oid))["payment_status"] == "unpaid"


async def test_webhook_payment_failed(client):
    oid, pi = _new_order()
    await client.post("/api/stripe/webhook", json=_event("payment_intent.payment_failed", pi))
    assert (await _order(client, oid))["payment_status"] == "failed"


async def test_failed_payment_can_still_succeed_on_retry(client):
    oid, pi = _new_order()
    await client.post("/api/stripe/webhook", json=_event("payment_intent.payment_failed", pi))
    await client.post("/api/stripe/webhook", json=_event("payment_intent.succeeded", pi, 900))
    assert (await _order(client, oid))["payment_status"] == "paid"


async def test_webhook_unknown_payment_intent_is_ignored(client):
    r = await client.post("/api/stripe/webhook", json=_event("payment_intent.succeeded", "pi_inconnu", 100))
    assert r.status_code == 200


async def test_unpaid_orders_never_count_in_stats_exports_or_dashboard(client, admin_headers, director_headers):
    before = (await client.get("/api/admin/stats", headers=admin_headers)).json()
    unpaid_id, _ = _new_order(items=[item(price=9.0, qty=1)])
    after = (await client.get("/api/admin/stats", headers=admin_headers)).json()
    assert after["orders_count"] == before["orders_count"]
    assert after["revenue"] == before["revenue"]
    assert after["pending_unpaid"] == before["pending_unpaid"] + 1

    ids = [o["id"] for o in (await client.get("/api/admin/orders", headers=admin_headers)).json()["orders"]]
    assert unpaid_id not in ids
    all_ids = [o["id"] for o in (await client.get("/api/admin/orders?payment=all", headers=admin_headers)).json()["orders"]]
    assert unpaid_id in all_ids

    csv = (await client.get("/api/admin/export/printer.csv", headers=admin_headers)).text
    assert unpaid_id[:8].upper() not in csv
    d_before = (await client.get("/api/director/dashboard", headers=director_headers)).json()["revenue"]
    assert d_before == round(after["revenue"])


async def test_paid_order_counts_everywhere(client, admin_headers):
    before = (await client.get("/api/admin/stats", headers=admin_headers)).json()
    oid, _ = _new_order(payment_status="paid", items=[item(price=9.0, qty=2)])
    after = (await client.get("/api/admin/stats", headers=admin_headers)).json()
    assert after["orders_count"] == before["orders_count"] + 1
    assert after["revenue"] == before["revenue"] + 18
    assert oid[:8].upper() in (await client.get("/api/admin/export/printer.csv", headers=admin_headers)).text


async def test_cancelling_a_batch_removes_it_from_the_numbers(client, admin_headers):
    oid, _ = _new_order(payment_status="paid")
    before = (await client.get("/api/admin/stats", headers=admin_headers)).json()["orders_count"]
    r = await client.patch(f"/api/admin/orders/{oid}", json={"batch_status": "cancelled"}, headers=admin_headers)
    assert r.status_code == 200
    assert (await client.get("/api/admin/stats", headers=admin_headers)).json()["orders_count"] == before - 1


async def test_orders_of_another_school_are_invisible_and_untouchable(client, admin_headers, other_admin_headers):
    oid, _ = _new_order(code=OTHER_CODE, school=OTHER_SCHOOL, payment_status="paid")
    mine = [o["id"] for o in (await client.get("/api/admin/orders", headers=admin_headers)).json()["orders"]]
    theirs = [o["id"] for o in (await client.get("/api/admin/orders", headers=other_admin_headers)).json()["orders"]]
    assert oid not in mine and oid in theirs
    r = await client.patch(f"/api/admin/orders/{oid}", json={"batch_status": "printed"}, headers=admin_headers)
    assert r.status_code == 404
