"""Tests for Google adapter conversion functions."""

import pytest

from dotpromptz.adapters.google import (
    GoogleAdapter,
    to_genai_contents,
    to_genai_request,
    to_genai_tools,
)
from dotpromptz.credentials import Credential
from dotpromptz.typing import (
    DataPart,
    MediaContent,
    MediaPart,
    Message,
    PromptOutputConfig,
    RenderedPrompt,
    Role,
    TextPart,
    ToolDefinition,
    ToolRequestContent,
    ToolRequestPart,
    ToolResponseContent,
    ToolResponsePart,
)


class TestSystemExtraction:
    """Test system message extraction."""

    def test_system_extracted(self) -> None:
        system_text, contents = to_genai_contents([
            Message(role=Role.SYSTEM, content=[TextPart(text='Be concise.')]),
            Message(role=Role.USER, content=[TextPart(text='hi')]),
        ])
        assert system_text == 'Be concise.'
        assert len(contents) == 1
        assert contents[0]['role'] == 'user'

    def test_no_system(self) -> None:
        system_text, contents = to_genai_contents([
            Message(role=Role.USER, content=[TextPart(text='hi')]),
        ])
        assert system_text is None


class TestContentConversion:
    """Test Part → Gemini parts conversion."""

    def test_text_part(self) -> None:
        _, contents = to_genai_contents([
            Message(role=Role.USER, content=[TextPart(text='hello')]),
        ])
        assert contents[0]['parts'] == [{'text': 'hello'}]

    def test_tool_request(self) -> None:
        _, contents = to_genai_contents([
            Message(
                role=Role.MODEL,
                content=[ToolRequestPart(tool_request=ToolRequestContent(name='search', input={'q': 'x'}))],
            ),
        ])
        parts = contents[0]['parts']
        assert parts[0]['function_call']['name'] == 'search'
        assert parts[0]['function_call']['args'] == {'q': 'x'}

    def test_tool_response(self) -> None:
        _, contents = to_genai_contents([
            Message(
                role=Role.TOOL,
                content=[ToolResponsePart(tool_response=ToolResponseContent(name='search', output={'r': 1}))],
            ),
        ])
        parts = contents[0]['parts']
        assert parts[0]['function_response']['name'] == 'search'


class TestRoleMapping:
    """Test role conversion for Gemini."""

    def test_model_role(self) -> None:
        _, contents = to_genai_contents([
            Message(role=Role.MODEL, content=[TextPart(text='ok')]),
        ])
        assert contents[0]['role'] == 'model'

    def test_user_role(self) -> None:
        _, contents = to_genai_contents([
            Message(role=Role.USER, content=[TextPart(text='hi')]),
        ])
        assert contents[0]['role'] == 'user'


class TestToolConversion:
    """Test ToolDefinition → Gemini tool format."""

    def test_basic(self) -> None:
        tools = to_genai_tools([
            ToolDefinition(name='search', description='desc', input_schema={'type': 'object'}),
        ])
        assert tools is not None
        assert len(tools) == 1
        assert tools[0]['function_declarations'][0]['name'] == 'search'

    def test_none(self) -> None:
        assert to_genai_tools(None) is None


class TestToGenAIRequest:
    """Test full RenderedPrompt → Gemini request conversion."""

    def test_basic(self) -> None:
        rendered = RenderedPrompt(
            config={'model': 'gemini-2.5-flash'},
            messages=[Message(role=Role.USER, content=[TextPart(text='hi')])],
        )
        req = to_genai_request(rendered)
        assert req['model'] == 'gemini-2.5-flash'
        assert len(req['contents']) == 1

    def test_system_in_config(self) -> None:
        rendered = RenderedPrompt(
            config={'model': 'gemini-2.5-flash'},
            messages=[
                Message(role=Role.SYSTEM, content=[TextPart(text='sys')]),
                Message(role=Role.USER, content=[TextPart(text='hi')]),
            ],
        )
        req = to_genai_request(rendered)
        assert req['config']['system_instruction'] == 'sys'

    def test_json_output(self) -> None:
        rendered = RenderedPrompt(
            config={'model': 'gemini-2.5-flash'},
            messages=[Message(role=Role.USER, content=[TextPart(text='hi')])],
            output=PromptOutputConfig(format='json', file_name='output'),
        )
        req = to_genai_request(rendered)
        assert req['config']['response_mime_type'] == 'application/json'
        assert 'response_json_schema' not in req.get('config', {})

    def test_json_output_with_schema(self) -> None:
        schema = {
            'type': 'object',
            'properties': {
                'name': {'type': 'string'},
                'age': {'type': 'integer'},
            },
            'required': ['name', 'age'],
            'additionalProperties': False,
        }
        rendered = RenderedPrompt(
            config={'model': 'gemini-2.5-flash'},
            messages=[Message(role=Role.USER, content=[TextPart(text='hi')])],
            output=PromptOutputConfig(format='json', schema=schema, file_name='output'),
        )
        req = to_genai_request(rendered)
        assert req['config']['response_mime_type'] == 'application/json'
        assert req['config']['response_json_schema'] == schema

    def test_txt_output_no_schema(self) -> None:
        rendered = RenderedPrompt(
            config={'model': 'gemini-2.5-flash'},
            messages=[Message(role=Role.USER, content=[TextPart(text='hi')])],
            output=PromptOutputConfig(format='txt', file_name='output'),
        )
        req = to_genai_request(rendered)
        assert 'response_json_schema' not in req.get('config', {})
        assert 'response_mime_type' not in req.get('config', {})

    def test_image_output_sets_response_modalities(self) -> None:
        rendered = RenderedPrompt(
            config={'model': 'gemini-2.0-flash-exp'},
            messages=[Message(role=Role.USER, content=[TextPart(text='draw a cat')])],
            output=PromptOutputConfig(format='image', file_name='output'),
        )
        req = to_genai_request(rendered)
        assert req['config']['response_modalities'] == ['IMAGE']

    def test_image_output_no_json_mime_type(self) -> None:
        rendered = RenderedPrompt(
            config={'model': 'gemini-2.0-flash-exp'},
            messages=[Message(role=Role.USER, content=[TextPart(text='draw a cat')])],
            output=PromptOutputConfig(format='image', file_name='output'),
        )
        req = to_genai_request(rendered)
        assert 'response_mime_type' not in req.get('config', {})

    def test_no_model_raises(self) -> None:
        rendered = RenderedPrompt(
            messages=[Message(role=Role.USER, content=[TextPart(text='hi')])],
        )
        with pytest.raises(ValueError, match='No model specified'):
            to_genai_request(rendered)


class TestGoogleAdapterConvert:
    """Test the adapter's convert method (no API call)."""

    def test_convert_returns_dict(self) -> None:
        cred = Credential(name='test', adapter='google', api_key='test-key')
        adapter = GoogleAdapter(credential=cred)
        rendered = RenderedPrompt(
            config={'model': 'gemini-2.5-flash'},
            messages=[Message(role=Role.USER, content=[TextPart(text='hello')])],
        )
        result = adapter.convert(rendered)
        assert isinstance(result, dict)
        assert result['model'] == 'gemini-2.5-flash'


class TestGoogleAdapterBaseUrl:
    """Test base_url from Credential is used correctly."""

    def test_credential_base_url(self) -> None:
        cred = Credential(name='test', adapter='google', api_key='test-key', base_url='https://my-proxy.example.com')
        adapter = GoogleAdapter(credential=cred)
        assert adapter._credential.base_url == 'https://my-proxy.example.com'

    def test_credential_no_base_url(self) -> None:
        cred = Credential(name='test', adapter='google', api_key='test-key')
        adapter = GoogleAdapter(credential=cred)
        assert adapter._credential.base_url is None


class TestMediaPartConversion:
    """Tests for _part_to_genai with different Part types."""

    def test_data_uri_media_part(self) -> None:
        from dotpromptz.adapters.google import _part_to_genai

        part = MediaPart(media=MediaContent(url='data:image/png;base64,abc123', content_type='image/png'))
        result = _part_to_genai(part)
        assert result is not None
        assert 'inline_data' in result
        assert result['inline_data']['mime_type'] == 'image/png'
        assert result['inline_data']['data'] == 'abc123'

    def test_http_url_media_part(self) -> None:
        from dotpromptz.adapters.google import _part_to_genai

        part = MediaPart(media=MediaContent(url='https://example.com/img.jpg', content_type='image/jpeg'))
        result = _part_to_genai(part)
        assert result is not None
        assert 'file_data' in result
        assert result['file_data']['file_uri'] == 'https://example.com/img.jpg'
        assert result['file_data']['mime_type'] == 'image/jpeg'

    def test_data_part_becomes_json_text(self) -> None:
        from dotpromptz.adapters.google import _part_to_genai

        part = DataPart(data={'key': 'value'})
        result = _part_to_genai(part)
        assert result is not None
        assert 'text' in result
        import json

        assert json.loads(result['text']) == {'key': 'value'}
