"""Tests for async resource operations across key API sections."""

import httpx
import pytest

from duffelbag._exceptions import NotFoundError, ValidationError
from duffelbag.models import (
    AirlineInitiatedChange,
    CustomerUser,
    CustomerUserGroup,
    OfferRequest,
    Order,
    OrderChange,
    OrderChangeOffer,
    OrderChangeRequest,
    Payment,
    Service,
    StaysBooking,
    StaysQuote,
    StaysSearchResult,
    Webhook,
)


# --- Offer Requests ---


async def test_async_create_offer_request(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(200, json={
            "data": {
                "id": "orq_001",
                "live_mode": False,
                "created_at": "2026-01-01T00:00:00Z",
                "cabin_class": "economy",
                "slices": [{
                    "origin_type": "airport",
                    "origin": {"id": "arp_lhr", "name": "Heathrow", "iata_code": "LHR"},
                    "destination_type": "airport",
                    "destination": {"id": "arp_jfk", "name": "JFK", "iata_code": "JFK"},
                    "departure_date": "2026-06-01",
                }],
                "offers": [{"id": "off_001", "total_amount": "350.00", "total_currency": "GBP"}],
                "passengers": [{"type": "adult"}],
                "client_key": "ck_001",
            }
        })
    )

    result = await async_client.offer_requests.create(
        slices=[{"origin": "LHR", "destination": "JFK", "departure_date": "2026-06-01"}],
        passengers=[{"type": "adult"}],
        cabin_class="economy",
    )

    assert isinstance(result, OfferRequest)
    assert result.id == "orq_001"
    assert result.cabin_class.value == "economy"

    req = async_mock_transport.last_request
    assert req.method == "POST"
    assert "/air/offer_requests" in str(req.url)
    assert "return_offers=true" in str(req.url)


async def test_async_get_offer_request(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(200, json={"data": {"id": "orq_001", "cabin_class": "business"}})
    )

    result = await async_client.offer_requests.get("orq_001")
    assert result.id == "orq_001"
    assert "/air/offer_requests/orq_001" in str(async_mock_transport.last_request.url)


async def test_async_list_offer_requests(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(200, json={
            "meta": {"limit": 50, "after": None},
            "data": [{"id": "orq_001"}, {"id": "orq_002"}],
        })
    )

    results = [r async for r in async_client.offer_requests.list()]
    assert len(results) == 2
    assert all(isinstance(r, OfferRequest) for r in results)


# --- Orders ---


async def test_async_create_order(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(201, json={
            "data": {
                "id": "ord_001",
                "booking_reference": "ABC123",
                "type": "instant",
                "total_amount": "350.00",
                "total_currency": "GBP",
                "passengers": [{
                    "id": "pas_001",
                    "given_name": "Jane",
                    "family_name": "Doe",
                    "type": "adult",
                }],
            }
        })
    )

    order = await async_client.orders.create(
        selected_offers=["off_001"],
        passengers=[{
            "id": "pas_001",
            "given_name": "Jane",
            "family_name": "Doe",
            "born_on": "1990-01-01",
            "gender": "f",
            "title": "Ms",
            "email": "jane@example.com",
            "phone_number": "+441234567890",
        }],
        payments=[{"type": "balance", "amount": "350.00", "currency": "GBP"}],
    )

    assert isinstance(order, Order)
    assert order.id == "ord_001"
    assert order.booking_reference == "ABC123"

    body = async_mock_transport.last_request_json()
    assert body["data"]["selected_offers"] == ["off_001"]


async def test_async_list_orders_with_filters(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(200, json={
            "meta": {"limit": 10, "after": None},
            "data": [{"id": "ord_001", "booking_reference": "XYZ"}],
        })
    )

    orders = [o async for o in async_client.orders.list(booking_reference="XYZ", limit=10)]
    assert len(orders) == 1
    url = str(async_mock_transport.last_request.url)
    assert "booking_reference=XYZ" in url


async def test_async_update_order_metadata(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(200, json={"data": {"id": "ord_001", "metadata": {"key": "val"}}})
    )

    order = await async_client.orders.update("ord_001", metadata={"key": "val"})
    assert order.metadata == {"key": "val"}
    assert async_mock_transport.last_request.method == "PATCH"


async def test_async_available_services(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(200, json={
            "data": [
                {"id": "srv_001", "type": "baggage", "total_amount": "25.00", "total_currency": "GBP"},
            ]
        })
    )

    services = await async_client.orders.available_services("ord_001")
    assert len(services) == 1
    assert isinstance(services[0], Service)
    assert services[0].type.value == "baggage"


async def test_async_add_service(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(200, json={"data": {"id": "ord_001"}})
    )

    await async_client.orders.add_service("ord_001", service_id="srv_001", quantity=1)
    req = async_mock_transport.last_request
    assert req.method == "POST"
    assert "/air/orders/ord_001/services" in str(req.url)


# --- Order Changes ---


async def test_async_create_order_change_request(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(200, json={
            "data": {
                "id": "ocr_001",
                "order_id": "ord_001",
                "order_change_offers": [{"id": "oco_001"}],
            }
        })
    )

    result = await async_client.order_change_requests.create(
        order_id="ord_001",
        slices={"remove": [{"slice_id": "sli_001"}], "add": []},
    )
    assert isinstance(result, OrderChangeRequest)
    assert result.id == "ocr_001"


async def test_async_list_order_change_offers(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(200, json={
            "meta": {"limit": 50, "after": None},
            "data": [{"id": "oco_001"}],
        })
    )

    offers = [o async for o in async_client.order_change_offers.list("ocr_001")]
    assert len(offers) == 1
    assert isinstance(offers[0], OrderChangeOffer)


# --- Payments ---


async def test_async_create_payment(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(200, json={
            "data": {"id": "pay_001", "type": "balance", "amount": "100.00", "currency": "GBP"}
        })
    )

    payment = await async_client.payments.create(
        order_id="ord_001", type="balance", amount="100.00", currency="GBP",
    )
    assert isinstance(payment, Payment)
    assert payment.id == "pay_001"


# --- Stays ---


async def test_async_create_stays_quote(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(200, json={"data": {"id": "quo_001", "status": "confirmed"}})
    )

    quote = await async_client.stays_quotes.create(rate_id="rat_001")
    assert isinstance(quote, StaysQuote)
    assert quote.id == "quo_001"


async def test_async_create_stays_booking(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(200, json={
            "data": {
                "id": "htl_bkg_001",
                "status": "confirmed",
                "check_in_date": "2026-06-01",
                "check_out_date": "2026-06-03",
            }
        })
    )

    booking = await async_client.stays_bookings.create(
        quote_id="quo_001",
        email="jane@example.com",
        phone_number="+441234567890",
        guests=[{"given_name": "Jane", "family_name": "Doe"}],
    )
    assert isinstance(booking, StaysBooking)
    assert booking.id == "htl_bkg_001"


async def test_async_cancel_stays_booking(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(200, json={"data": {"id": "htl_bkg_001", "status": "cancelled"}})
    )

    booking = await async_client.stays_bookings.cancel("htl_bkg_001")
    assert booking.status == "cancelled"
    req = async_mock_transport.last_request
    assert req.method == "POST"
    assert "/actions/cancel" in str(req.url)


# --- Webhooks ---


async def test_async_create_webhook(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(200, json={
            "data": {
                "id": "wbk_001",
                "url": "https://example.com/webhook",
                "events": ["order.created"],
                "active": True,
            }
        })
    )

    webhook = await async_client.webhooks.create(
        url="https://example.com/webhook", events=["order.created"],
    )
    assert isinstance(webhook, Webhook)
    assert webhook.id == "wbk_001"


async def test_async_delete_webhook(async_client, async_mock_transport):
    async_mock_transport.queue_response(httpx.Response(204))

    await async_client.webhooks.delete("wbk_001")
    req = async_mock_transport.last_request
    assert req.method == "DELETE"


async def test_async_ping_webhook(async_client, async_mock_transport):
    async_mock_transport.queue_response(httpx.Response(204))

    await async_client.webhooks.ping("wbk_001")
    assert "/actions/ping" in str(async_mock_transport.last_request.url)


# --- Identity ---


async def test_async_create_customer_user(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(200, json={
            "data": {
                "id": "cus_001",
                "email": "jane@example.com",
                "given_name": "Jane",
                "family_name": "Doe",
            }
        })
    )

    user = await async_client.customer_users.create(
        email="jane@example.com", given_name="Jane", family_name="Doe",
    )
    assert isinstance(user, CustomerUser)
    assert user.id == "cus_001"


async def test_async_create_customer_user_group(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(200, json={"data": {"id": "cug_001", "name": "VIPs"}})
    )

    group = await async_client.customer_user_groups.create(name="VIPs", user_ids=["cus_001"])
    assert isinstance(group, CustomerUserGroup)
    body = async_mock_transport.last_request_json()
    assert body["data"]["user_ids"] == ["cus_001"]


# --- Exceptions (async) ---


async def test_async_404_raises_not_found(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(404, json={
            "errors": [{"type": "invalid_request_error", "title": "Not Found", "message": "Resource not found", "code": "not_found"}],
        })
    )

    with pytest.raises(NotFoundError):
        await async_client.orders.get("ord_nonexistent")


async def test_async_422_raises_validation_error(async_client, async_mock_transport):
    async_mock_transport.queue_response(
        httpx.Response(422, json={
            "errors": [{"type": "validation_error", "title": "Invalid", "message": "Bad input"}],
            "meta": {"request_id": "req_002", "status": 422},
        })
    )

    with pytest.raises(ValidationError):
        await async_client.offer_requests.create(slices=[], passengers=[])
