"""Phase 7.2 — Point Engine tests."""
from __future__ import annotations

import pytest

from app.events.publisher import get_event_publisher
from app.events.types import LoyaltyEventType
from app.tests.conftest import TENANT_A, tenant_headers


async def _open_account(client, *, suffix="a"):
    program = await client.post(
        "/api/v1/programs",
        json={"code": f"pts{suffix}", "name": f"Points {suffix}"},
        headers=tenant_headers(TENANT_A),
    )
    assert program.status_code == 201, program.text
    program_id = program.json()["id"]
    member = await client.post(
        "/api/v1/members",
        json={"program_id": program_id, "display_name": "P", "enroll": True},
        headers=tenant_headers(TENANT_A),
    )
    member_id = member.json()["id"]
    account = await client.post(
        "/api/v1/point-accounts",
        json={"program_id": program_id, "member_id": member_id},
        headers=tenant_headers(TENANT_A),
    )
    assert account.status_code == 201, account.text
    return account.json()["id"]


@pytest.mark.asyncio
async def test_earn_redeem_balance_and_idempotency(client):
    account_id = await _open_account(client)
    earn = await client.post(
        f"/api/v1/point-accounts/{account_id}/earn",
        json={"amount": 100, "reason": "purchase", "idempotency_key": "e1"},
        headers=tenant_headers(TENANT_A),
    )
    assert earn.status_code == 200, earn.text
    assert earn.json()["balance_after"] == 100

    again = await client.post(
        f"/api/v1/point-accounts/{account_id}/earn",
        json={"amount": 100, "reason": "purchase", "idempotency_key": "e1"},
        headers=tenant_headers(TENANT_A),
    )
    assert again.status_code == 200
    assert again.json()["id"] == earn.json()["id"]

    bal = await client.get(
        f"/api/v1/point-accounts/{account_id}/balance",
        headers=tenant_headers(TENANT_A),
    )
    assert bal.status_code == 200
    assert bal.json()["balance"] == 100
    assert "balance" in bal.json()

    redeem = await client.post(
        f"/api/v1/point-accounts/{account_id}/redeem",
        json={"amount": 40, "reason": "reward"},
        headers=tenant_headers(TENANT_A),
    )
    assert redeem.status_code == 200
    assert redeem.json()["balance_after"] == 60

    over = await client.post(
        f"/api/v1/point-accounts/{account_id}/redeem",
        json={"amount": 1000},
        headers=tenant_headers(TENANT_A),
    )
    assert over.status_code == 409
    assert over.json()["error"]["code"] == "insufficient_points"

    events = {e.event_type for e in get_event_publisher().published}
    assert LoyaltyEventType.POINTS_EARNED.value in events
    assert LoyaltyEventType.POINTS_REDEEMED.value in events


@pytest.mark.asyncio
async def test_adjust_expire_and_ledger_list(client):
    account_id = await _open_account(client, suffix="adj")
    await client.post(
        f"/api/v1/point-accounts/{account_id}/earn",
        json={"amount": 50},
        headers=tenant_headers(TENANT_A),
    )
    adj = await client.post(
        f"/api/v1/point-accounts/{account_id}/adjust",
        json={"amount": -10, "reason": "correction"},
        headers=tenant_headers(TENANT_A),
    )
    assert adj.status_code == 200
    assert adj.json()["balance_after"] == 40

    exp = await client.post(
        f"/api/v1/point-accounts/{account_id}/expire-points",
        json={"amount": 5, "reason": "aged"},
        headers=tenant_headers(TENANT_A),
    )
    assert exp.status_code == 200
    assert exp.json()["balance_after"] == 35

    page = await client.get(
        f"/api/v1/point-accounts/{account_id}/ledger",
        params={"limit": 10},
        headers=tenant_headers(TENANT_A),
    )
    assert page.status_code == 200
    body = page.json()
    assert body["total"] >= 3
    assert len(body["items"]) >= 3
