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

from __future__ import annotations

import pytest
from pydantic import ValidationError

from dotpromptz.adapters import GenerateResponse, ToolCallResult, Usage, get_adapter, list_adapters
from dotpromptz.adapters._base import Adapter, 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_image_format_requires_save_path(self) -> None:
        """format='image' without save_path must raise ValidationError."""
        with pytest.raises(ValidationError, match='save_path'):
            PromptOutputConfig(format='image')

    def test_image_format_with_save_path_ok(self) -> None:
        """format='image' with save_path should succeed."""
        cfg = PromptOutputConfig(format='image', save_path='/tmp/out.png')
        assert cfg.format == 'image'
        assert cfg.save_path == '/tmp/out.png'

    def test_non_image_format_without_save_path_ok(self) -> None:
        """Non-image formats should not require save_path."""
        cfg = PromptOutputConfig(format='json')
        assert cfg.format == 'json'
        assert cfg.save_path is None

    def test_text_format_ok(self) -> None:
        cfg = PromptOutputConfig(format='text')
        assert cfg.format == 'text'

    def test_none_format_ok(self) -> None:
        cfg = PromptOutputConfig()
        assert cfg.format is None
        assert cfg.save_path is None

    def test_image_format_empty_save_path_raises(self) -> None:
        """format='image' with empty string save_path must also fail."""
        with pytest.raises(ValidationError, match='save_path'):
            PromptOutputConfig(format='image', save_path='')


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

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

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

    def test_custom_mime_type(self) -> None:
        img = ImageResult(data=b'\xff', mime_type='image/jpeg', saved_path='/tmp/x.jpg')
        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', saved_path='/tmp/out.png')
        resp = GenerateResponse(image=img)
        assert resp.image is not None
        assert resp.image.saved_path == '/tmp/out.png'
        assert resp.text is None

    def test_image_and_text(self) -> None:
        """Both text and image can coexist."""
        img = ImageResult(data=b'\xff', saved_path='/tmp/x.png')
        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
