"""End-to-end integration tests – Dotprompt render → OpenAI adapter generate."""

from __future__ import annotations

import pytest

from dotpromptz.typing import (
    DataArgument,
    Message,
    RenderedPrompt,
    Role,
    TextPart,
    ToolDefinition,
)

pytestmark = pytest.mark.integration


# ---------------------------------------------------------------------------
# Test A – adapter.generate() with richer config
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_adapter_generate_with_temperature(openai_adapter) -> None:
    """Call generate() with explicit temperature / max_tokens."""
    rendered = RenderedPrompt(
        config={'model': 'gpt-4.1', 'temperature': 0.0, 'maxTokens': 30},
        messages=[
            Message(role=Role.SYSTEM, content=[TextPart(text='You are a concise assistant.')]),
            Message(role=Role.USER, content=[TextPart(text='What is 2 + 2?')]),
        ],
    )

    response = await openai_adapter.generate(rendered)

    assert response.text is not None
    assert '4' in response.text
    assert response.model is not None
    assert response.usage is not None
    assert response.usage.prompt_tokens > 0
    assert response.usage.completion_tokens > 0
    print(f'\n[e2e] model={response.model}  text={response.text!r}')


# ---------------------------------------------------------------------------
# Test B – full Dotprompt → render → generate pipeline
# ---------------------------------------------------------------------------

_PROMPT_SOURCE = """\
---
model: gpt-4.1
config:
  temperature: 0
---
{{role "system"}}
You are a geography expert. Answer in one sentence.
{{role "user"}}
What is the capital of {{country}}?
"""


@pytest.mark.asyncio
async def test_dotprompt_render_then_generate(dotprompt_instance, openai_adapter) -> None:
    """Render a .prompt template and send it through the OpenAI adapter."""
    rendered = await dotprompt_instance.render(
        _PROMPT_SOURCE,
        data=DataArgument(input={'country': 'France'}),
    )

    # Verify rendering produced the expected messages
    assert len(rendered.messages) >= 2
    assert rendered.messages[0].role == Role.SYSTEM
    assert rendered.messages[-1].role == Role.USER

    # Call the real API
    response = await openai_adapter.generate(rendered)

    assert response.text is not None
    assert 'paris' in response.text.lower()
    print(f'\n[e2e] rendered → response: {response.text!r}')


# ---------------------------------------------------------------------------
# Test C – multi-turn conversation
# ---------------------------------------------------------------------------

_MULTITURN_SOURCE = """\
---
model: gpt-4.1
config:
  temperature: 0
---
{{role "system"}}
You are a math tutor. Be concise.
{{role "user"}}
What is 10 * 5?
{{role "model"}}
50
{{role "user"}}
Now divide that by 2.
"""


@pytest.mark.asyncio
async def test_multiturn_conversation(dotprompt_instance, openai_adapter) -> None:
    """Render a multi-turn prompt and verify the model follows context."""
    rendered = await dotprompt_instance.render(
        _MULTITURN_SOURCE,
        data=DataArgument(),
    )

    # Should have system + user + assistant + user = 4 messages
    assert len(rendered.messages) >= 4

    response = await openai_adapter.generate(rendered)

    assert response.text is not None
    assert '25' in response.text
    print(f'\n[e2e] multi-turn response: {response.text!r}')


# ---------------------------------------------------------------------------
# Test D – tool calling (function calling)
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_tool_calling(openai_adapter) -> None:
    """Verify the model returns a tool call when tools are provided."""
    rendered = RenderedPrompt(
        config={'model': 'gpt-4.1', 'temperature': 0},
        messages=[
            Message(
                role=Role.SYSTEM,
                content=[TextPart(text='You are a helpful assistant. Use the provided tools when appropriate.')],
            ),
            Message(
                role=Role.USER,
                content=[TextPart(text='What is the weather in Tokyo right now?')],
            ),
        ],
        tool_defs=[
            ToolDefinition(
                name='get_weather',
                description='Get the current weather for a city.',
                input_schema={
                    'type': 'object',
                    'properties': {
                        'city': {'type': 'string', 'description': 'The city name'},
                    },
                    'required': ['city'],
                },
            ),
        ],
    )

    response = await openai_adapter.generate(rendered)

    # The model should request the get_weather tool
    assert len(response.tool_calls) > 0
    tc = response.tool_calls[0]
    assert tc.name == 'get_weather'
    assert 'tokyo' in tc.arguments.get('city', '').lower()
    assert response.finish_reason == 'tool_calls'
    print(f'\n[e2e] tool_call: {tc.name}({tc.arguments})')
