import asyncio

import pytest
from sqlalchemy import func, select

from orders_api.models import Order, OutboxEvent

pytestmark = pytest.mark.anyio


async def count(state, model) -> int:
    async with state["sessions"]() as session:
        return await session.scalar(select(func.count()).select_from(model))


async def test_a_retry_replays_the_first_response(create_order, asha, state):
    first = await create_order(asha, key="checkout-42")
    retry = await create_order(asha, key="checkout-42")
    assert first.status_code == retry.status_code == 201
    assert retry.json() == first.json()
    assert retry.headers["idempotent-replayed"] == "true"
    assert await count(state, Order) == 1
    assert await count(state, OutboxEvent) == 1                   # and only one webhook


async def test_same_key_different_request_is_rejected(create_order, asha):
    await create_order(asha, key="checkout-42")
    r = await create_order(asha, {"items": [{"sku": "mocha", "quantity": 5}]}, key="checkout-42")
    assert r.status_code == 422 and r.json()["type"].endswith("idempotency-key-reused")


async def test_keys_are_per_user(create_order, asha, ravi, state):
    await create_order(asha, key="checkout-42")
    r = await create_order(ravi, key="checkout-42")
    assert r.status_code == 201 and r.json()["customerId"] == 2
    assert await count(state, Order) == 2


async def test_missing_key_is_a_400(client, asha):
    r = await client.post("/v1/orders", json={"items": [{"sku": "latte", "quantity": 1}]}, headers=asha)
    assert r.status_code == 400 and r.json()["type"].endswith("idempotency-key-required")


async def test_concurrent_duplicates_create_one_order(create_order, asha, state):
    responses = await asyncio.gather(*(create_order(asha, key="double-click") for _ in range(5)))
    assert {r.status_code for r in responses} == {201}
    assert len({r.json()["id"] for r in responses}) == 1
    assert await count(state, Order) == 1
