"""Tests for resources/voices.py."""

from __future__ import annotations

import httpx
import pytest
import respx

from murmr._client import AsyncMurmrClient, MurmrClient
from murmr._errors import MurmrError
from tests.helpers import BASE_URL, make_sse_body


class TestAsyncVoices:
    @respx.mock
    @pytest.mark.asyncio
    async def test_design_returns_wav(self) -> None:
        from murmr._audio import create_wav_header

        pcm = b"\x01\x02" * 100
        wav_bytes = create_wav_header(len(pcm)) + pcm

        respx.post(f"{BASE_URL}/v1/voices/design").mock(
            return_value=httpx.Response(200, content=wav_bytes)
        )

        async with AsyncMurmrClient(api_key="test_key", base_url=BASE_URL) as client:
            wav = await client.voices.design(
                input="Hello world",
                voice_description="A warm female voice",
            )
            assert wav[:4] == b"RIFF"
            assert wav == wav_bytes

    @respx.mock
    @pytest.mark.asyncio
    async def test_design_sends_correct_body(self) -> None:
        respx.post(f"{BASE_URL}/v1/voices/design").mock(
            return_value=httpx.Response(200, content=b"RIFF\x00\x00\x00\x00WAVEfmt ")
        )

        async with AsyncMurmrClient(api_key="test_key", base_url=BASE_URL) as client:
            await client.voices.design(
                input="Test text",
                voice_description="Deep male voice",
                language="French",
            )

        request = respx.calls.last.request
        import json

        body = json.loads(request.content)
        assert body["input"] == "Test text"
        assert body["voice_description"] == "Deep male voice"
        assert body["language"] == "French"

    @pytest.mark.asyncio
    async def test_design_validates_empty_input(self) -> None:
        async with AsyncMurmrClient(api_key="test_key", base_url=BASE_URL) as client:
            with pytest.raises(MurmrError, match="required and cannot be empty"):
                await client.voices.design(input="", voice_description="A voice")

    @pytest.mark.asyncio
    async def test_design_validates_empty_description(self) -> None:
        async with AsyncMurmrClient(api_key="test_key", base_url=BASE_URL) as client:
            with pytest.raises(MurmrError, match="voice_description is required"):
                await client.voices.design(input="Hello", voice_description="")

    @respx.mock
    @pytest.mark.asyncio
    async def test_design_stream(self) -> None:
        pcm1 = b"\x01\x00" * 50
        pcm2 = b"\x02\x00" * 50
        sse_body = make_sse_body(pcm1, pcm2)

        respx.post(f"{BASE_URL}/v1/voices/design/stream").mock(
            return_value=httpx.Response(200, content=sse_body)
        )

        async with AsyncMurmrClient(api_key="test_key", base_url=BASE_URL) as client:
            chunks_received: list[bytes] = []
            async with client.voices.design_stream(
                input="Hello", voice_description="Calm voice"
            ) as stream:
                async for chunk in stream:
                    if chunk.audio_bytes:
                        chunks_received.append(chunk.audio_bytes)

            assert len(chunks_received) == 2
            assert chunks_received[0] == pcm1
            assert chunks_received[1] == pcm2


class TestSyncVoices:
    @respx.mock
    def test_design_returns_wav(self) -> None:
        from murmr._audio import create_wav_header

        pcm = b"\x03\x04" * 100
        wav_bytes = create_wav_header(len(pcm)) + pcm

        respx.post(f"{BASE_URL}/v1/voices/design").mock(
            return_value=httpx.Response(200, content=wav_bytes)
        )

        with MurmrClient(api_key="test_key", base_url=BASE_URL) as client:
            wav = client.voices.design(
                input="Hello world",
                voice_description="A warm voice",
            )
            assert wav[:4] == b"RIFF"
            assert wav == wav_bytes

    @respx.mock
    def test_design_stream_sync(self) -> None:
        pcm = b"\x05\x06" * 75
        sse_body = make_sse_body(pcm)

        respx.post(f"{BASE_URL}/v1/voices/design/stream").mock(
            return_value=httpx.Response(200, content=sse_body)
        )

        with MurmrClient(api_key="test_key", base_url=BASE_URL) as client:
            chunks_received: list[bytes] = []
            with client.voices.design_stream(
                input="Hello", voice_description="A voice"
            ) as stream:
                for chunk in stream:
                    if chunk.audio_bytes:
                        chunks_received.append(chunk.audio_bytes)

            assert len(chunks_received) == 1
            assert chunks_received[0] == pcm


class TestErrorHandling:
    @respx.mock
    @pytest.mark.asyncio
    async def test_api_error_parsed(self) -> None:
        respx.post(f"{BASE_URL}/v1/voices/design").mock(
            return_value=httpx.Response(
                429,
                json={
                    "error": {
                        "message": "Rate limit exceeded",
                        "type": "rate_limit_exceeded",
                        "code": "rate_limit",
                    }
                },
                headers={
                    "X-Concurrent-Limit": "3",
                    "X-Concurrent-Active": "3",
                },
            )
        )

        async with AsyncMurmrClient(api_key="test_key", base_url=BASE_URL) as client:
            with pytest.raises(MurmrError) as exc_info:
                await client.voices.design(
                    input="Hello", voice_description="A voice"
                )
            err = exc_info.value
            assert err.status == 429
            assert err.type == "rate_limit_exceeded"
            assert err.concurrent_limit == 3
            assert err.concurrent_active == 3
