"""Tests for the adapter base types and registry."""

import pytest
from pydantic import ValidationError

from dotpromptz.adapters import GenerateResponse, ToolCallResult, Usage, get_adapter, list_adapters
from dotpromptz.adapters._base import ImageResult
from dotpromptz.typing import PromptOutputConfig


class TestUsage:
    """Tests for the Usage model."""

    def test_defaults(self) -> None:
        usage = Usage()
        assert usage.prompt_tokens == 0
        assert usage.completion_tokens == 0
        assert usage.total_tokens == 0

    def test_values(self) -> None:
        usage = Usage(prompt_tokens=10, completion_tokens=20, total_tokens=30)
        assert usage.total_tokens == 30


class TestToolCallResult:
    """Tests for the ToolCallResult model."""

    def test_basic(self) -> None:
        tc = ToolCallResult(id='call_1', name='search', arguments={'query': 'test'})
        assert tc.name == 'search'
        assert tc.arguments == {'query': 'test'}


class TestGenerateResponse:
    """Tests for the GenerateResponse model."""

    def test_text_only(self) -> None:
        resp = GenerateResponse(text='Hello!')
        assert resp.text == 'Hello!'
        assert resp.tool_calls == []
        assert resp.usage is None

    def test_with_tool_calls(self) -> None:
        tc = ToolCallResult(id='1', name='fn', arguments={})
        resp = GenerateResponse(tool_calls=[tc])
        assert len(resp.tool_calls) == 1


class TestRegistry:
    """Tests for the adapter registry."""

    def test_list_adapters(self) -> None:
        names = list_adapters()
        assert 'openai' in names
        assert 'anthropic' in names
        assert 'google' in names
        assert len(names) == 3

    def test_get_unknown_adapter(self) -> None:
        with pytest.raises(ValueError, match='Unknown adapter'):
            get_adapter('nonexistent')


class TestPromptOutputConfig:
    """Tests for PromptOutputConfig validation."""

    def test_json_format_ok(self) -> None:
        cfg = PromptOutputConfig(format='json', file_name='output')
        assert cfg.format == 'json'

    def test_yaml_format_ok(self) -> None:
        cfg = PromptOutputConfig(format='yaml', file_name='output')
        assert cfg.format == 'yaml'

    def test_txt_format_ok(self) -> None:
        cfg = PromptOutputConfig(format='txt', file_name='output')
        assert cfg.format == 'txt'

    def test_image_format_ok(self) -> None:
        cfg = PromptOutputConfig(format='image', file_name='output')
        assert cfg.format == 'image'

    def test_schema_only_for_json_or_yaml(self) -> None:
        """schema is only valid when format is 'json' or 'yaml'."""
        cfg = PromptOutputConfig(format='json', schema={'type': 'object'}, file_name='output')
        assert cfg.schema_ == {'type': 'object'}
        cfg2 = PromptOutputConfig(format='yaml', schema={'type': 'object'}, file_name='output')
        assert cfg2.schema_ == {'type': 'object'}

    def test_schema_with_txt_raises(self) -> None:
        with pytest.raises(ValidationError, match='schema'):
            PromptOutputConfig(format='txt', schema={'type': 'object'}, file_name='output')

    def test_schema_with_image_raises(self) -> None:
        with pytest.raises(ValidationError, match='schema'):
            PromptOutputConfig(format='image', schema={'type': 'object'}, file_name='output')

    def test_file_name_and_output_dir(self) -> None:
        cfg = PromptOutputConfig(format='json', output_dir='./out', file_name='result')
        assert cfg.output_dir == './out'
        assert cfg.file_name == 'result'

    def test_aggregate_default_false(self) -> None:
        cfg = PromptOutputConfig(format='json', file_name='output')
        assert cfg.aggregate is False

    def test_aggregate_true(self) -> None:
        cfg = PromptOutputConfig(format='json', aggregate=True, file_name='output')
        assert cfg.aggregate is True


class TestImageResult:
    """Tests for the ImageResult model."""

    def test_basic(self) -> None:
        img = ImageResult(data=b'\x89PNG', mime_type='image/png')
        assert img.data == b'\x89PNG'
        assert img.mime_type == 'image/png'

    def test_default_mime_type(self) -> None:
        img = ImageResult(data=b'\xff')
        assert img.mime_type == 'image/png'

    def test_custom_mime_type(self) -> None:
        img = ImageResult(data=b'\xff', mime_type='image/jpeg')
        assert img.mime_type == 'image/jpeg'


class TestGenerateResponseImage:
    """Tests for GenerateResponse with image field."""

    def test_default_no_image(self) -> None:
        resp = GenerateResponse(text='hello')
        assert resp.image is None

    def test_with_image(self) -> None:
        img = ImageResult(data=b'\x89PNG', mime_type='image/png')
        resp = GenerateResponse(image=img)
        assert resp.image is not None
        assert resp.image.data == b'\x89PNG'
        assert resp.text is None

    def test_image_and_text(self) -> None:
        """Both text and image can coexist."""
        img = ImageResult(data=b'\xff')
        resp = GenerateResponse(text='caption', image=img)
        assert resp.text == 'caption'
        assert resp.image is not None


class TestParseDataUri:
    """Tests for the parse_data_uri helper."""

    def test_png_data_uri(self) -> None:
        from dotpromptz.adapters._base import parse_data_uri

        result = parse_data_uri('data:image/png;base64,abc123')
        assert result == ('image/png', 'abc123')

    def test_jpeg_data_uri(self) -> None:
        from dotpromptz.adapters._base import parse_data_uri

        result = parse_data_uri('data:image/jpeg;base64,xyz')
        assert result == ('image/jpeg', 'xyz')

    def test_non_data_uri_returns_none(self) -> None:
        from dotpromptz.adapters._base import parse_data_uri

        assert parse_data_uri('https://example.com/img.png') is None

    def test_empty_data(self) -> None:
        from dotpromptz.adapters._base import parse_data_uri

        result = parse_data_uri('data:image/png;base64,')
        assert result == ('image/png', '')

    def test_data_with_commas(self) -> None:
        from dotpromptz.adapters._base import parse_data_uri

        result = parse_data_uri('data:text/plain;base64,a,b,c')
        assert result == ('text/plain', 'a,b,c')

    def test_plain_text_not_data_uri(self) -> None:
        from dotpromptz.adapters._base import parse_data_uri

        assert parse_data_uri('not-a-data-uri') is None

    def test_empty_string(self) -> None:
        from dotpromptz.adapters._base import parse_data_uri

        assert parse_data_uri('') is None
