from unittest.mock import AsyncMock, patch

import pytest

from app.agents.tools.takeaway import (
    create_order_impl,
    get_payment_link_impl,
    search_menu_impl,
)

# ---------------------------------------------------------------------------
# search_menu_impl
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_search_menu_returns_formatted_results(fake_ctx):
    mock_results = [
        {"content": "Margherita Pizza", "metadata": {"price": 12.50}, "score": 0.92},
    ]
    with (
        patch(
            "app.agents.tools.takeaway.generate_embedding",
            new_callable=AsyncMock,
            return_value=[0.1] * 1536,
        ),
        patch(
            "app.agents.tools.takeaway.hybrid_search",
            new_callable=AsyncMock,
            return_value=mock_results,
        ) as mock_search,
    ):
        result = await search_menu_impl(fake_ctx, "pizza")

        assert len(result) == 1
        assert result[0]["content"] == "Margherita Pizza"
        assert result[0]["metadata"]["price"] == 12.50
        call_kwargs = mock_search.call_args.kwargs
        assert call_kwargs["source"] == "menu"
        assert call_kwargs["limit"] == 8


@pytest.mark.asyncio
async def test_search_menu_passes_session_and_restaurant(fake_ctx):
    with (
        patch(
            "app.agents.tools.takeaway.generate_embedding",
            new_callable=AsyncMock,
            return_value=[0.1] * 10,
        ),
        patch(
            "app.agents.tools.takeaway.hybrid_search",
            new_callable=AsyncMock,
            return_value=[],
        ) as mock_search,
    ):
        await search_menu_impl(fake_ctx, "pasta")

        call_kwargs = mock_search.call_args.kwargs
        assert call_kwargs["session"] is fake_ctx.deps.session
        assert call_kwargs["restaurant_id"] == "test-restaurant-id"
        assert call_kwargs["query_text"] == "pasta"


@pytest.mark.asyncio
async def test_search_menu_empty_results(fake_ctx):
    with (
        patch(
            "app.agents.tools.takeaway.generate_embedding",
            new_callable=AsyncMock,
            return_value=[0.1] * 10,
        ),
        patch(
            "app.agents.tools.takeaway.hybrid_search",
            new_callable=AsyncMock,
            return_value=[],
        ),
    ):
        result = await search_menu_impl(fake_ctx, "nonexistent")
        assert result == []


@pytest.mark.asyncio
async def test_search_menu_missing_metadata_defaults_to_empty(fake_ctx):
    """metadata=None in a result should become {}."""
    mock_results = [
        {"content": "Water", "metadata": None, "score": 0.5},
    ]
    with (
        patch(
            "app.agents.tools.takeaway.generate_embedding",
            new_callable=AsyncMock,
            return_value=[0.1] * 10,
        ),
        patch(
            "app.agents.tools.takeaway.hybrid_search",
            new_callable=AsyncMock,
            return_value=mock_results,
        ),
    ):
        result = await search_menu_impl(fake_ctx, "water")
        assert result[0]["metadata"] == {}


# ---------------------------------------------------------------------------
# create_order_impl
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_create_order_computes_total(fake_ctx):
    items = [
        {"name": "Pizza", "unit_price_cents": 1250, "quantity": 2},
        {"name": "Cola", "unit_price_cents": 300, "quantity": 1},
    ]
    with patch("app.agents.tools.takeaway.restate_proxy", new_callable=AsyncMock) as mock_proxy:
        result = await create_order_impl(fake_ctx, "test@example.com", items)

        assert result["status"] == "pending"
        assert result["total_amount"] == "28.00"
        assert "order_id" in result
        mock_proxy.assert_awaited_once()
        call_args = mock_proxy.call_args
        payload = call_args.args[4]
        assert payload["total_cents"] == 2800
        assert payload["customer_email"] == "test@example.com"
        assert payload["delivery_type"] == "pickup"


@pytest.mark.asyncio
async def test_create_order_with_delivery(fake_ctx):
    items = [{"name": "Salad", "unit_price_cents": 800, "quantity": 1}]
    with patch("app.agents.tools.takeaway.restate_proxy", new_callable=AsyncMock):
        result = await create_order_impl(
            fake_ctx, "a@b.com", items, delivery_type="delivery", notes="Ring bell"
        )
        assert result["total_amount"] == "8.00"


@pytest.mark.asyncio
async def test_create_order_empty_items(fake_ctx):
    with patch("app.agents.tools.takeaway.restate_proxy", new_callable=AsyncMock):
        result = await create_order_impl(fake_ctx, "a@b.com", [])
        assert result["total_amount"] == "0.00"


@pytest.mark.asyncio
async def test_create_order_calls_restate_with_object_mode(fake_ctx):
    items = [{"name": "Fries", "unit_price_cents": 400, "quantity": 1}]
    with patch("app.agents.tools.takeaway.restate_proxy", new_callable=AsyncMock) as mock_proxy:
        result = await create_order_impl(fake_ctx, "x@y.com", items)

        call_args = mock_proxy.call_args
        assert call_args.args[1] == "OrderObject"
        assert call_args.args[2] == result["order_id"]
        assert call_args.args[3] == "create"
        assert call_args.kwargs["mode"] == "object"


@pytest.mark.asyncio
async def test_create_order_passes_notes(fake_ctx):
    items = [{"name": "Soup", "unit_price_cents": 600, "quantity": 1}]
    with patch("app.agents.tools.takeaway.restate_proxy", new_callable=AsyncMock) as mock_proxy:
        await create_order_impl(fake_ctx, "x@y.com", items, notes="Extra spicy")
        payload = mock_proxy.call_args.args[4]
        assert payload["notes"] == "Extra spicy"


# ---------------------------------------------------------------------------
# get_payment_link_impl
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_get_payment_link_success(fake_ctx):
    mock_result = {"checkout_url": "https://mollie.com/pay/abc"}
    with patch(
        "app.payments.mollie.create_payment",
        new_callable=AsyncMock,
        return_value=mock_result,
    ):
        result = await get_payment_link_impl(fake_ctx, "order-123", 1500)
        assert result["checkout_url"] == "https://mollie.com/pay/abc"


@pytest.mark.asyncio
async def test_get_payment_link_not_configured(fake_ctx):
    from app.payments.mollie import PaymentNotConfiguredError

    exc = PaymentNotConfiguredError()
    with patch(
        "app.payments.mollie.create_payment",
        new_callable=AsyncMock,
        side_effect=exc,
    ):
        result = await get_payment_link_impl(fake_ctx, "order-123", 1500)
        assert result["status"] == "error"
        assert result["code"] == "payment_provider_not_configured"
        assert result["provider"] == "mollie"
