"""Tests for long-form speech generation."""

from __future__ import annotations

import httpx
import pytest
import respx

from murmr._audio import WAV_HEADER_SIZE
from murmr._client import AsyncMurmrClient, MurmrClient
from murmr._errors import MurmrChunkError
from tests.helpers import BASE_URL, make_sse_body


class TestAsyncLongForm:
    @respx.mock
    @pytest.mark.asyncio
    async def test_short_text_single_chunk(self) -> None:
        """Short text should produce a single chunk request."""
        pcm = b"\x01\x02" * 100
        sse_body = make_sse_body(pcm)

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

        async with AsyncMurmrClient(api_key="test_key", base_url=BASE_URL) as client:
            result = await client.speech.create_long_form(
                input="Hello world", voice="voice_abc"
            )
            assert result.total_chunks == 1
            assert result.audio[:4] == b"RIFF"
            assert result.character_count == len("Hello world")

    @respx.mock
    @pytest.mark.asyncio
    async def test_multi_chunk_concatenation(self) -> None:
        """Multiple chunks should be concatenated into a single WAV."""
        pcm1 = b"\x01\x00" * 50
        pcm2 = b"\x02\x00" * 50

        # First chunk request
        call_count = 0

        def mock_handler(request: httpx.Request) -> httpx.Response:
            nonlocal call_count
            pcm = pcm1 if call_count == 0 else pcm2
            call_count += 1
            return httpx.Response(200, content=make_sse_body(pcm))

        respx.post(f"{BASE_URL}/v1/audio/speech/stream").mock(side_effect=mock_handler)

        # Build text that splits into 2 chunks
        s1 = "First sentence " + "a" * 2000 + "."
        s2 = "Second sentence " + "b" * 2000 + "."
        text = f"{s1} {s2}"

        async with AsyncMurmrClient(api_key="test_key", base_url=BASE_URL) as client:
            result = await client.speech.create_long_form(
                input=text, voice="voice_abc"
            )
            assert result.total_chunks == 2
            assert result.audio[:4] == b"RIFF"
            assert len(result.audio) > WAV_HEADER_SIZE

    @respx.mock
    @pytest.mark.asyncio
    async def test_progress_callback(self) -> None:
        """on_progress should be called for each chunk."""
        pcm = b"\x00\x00" * 50
        sse_body = make_sse_body(pcm)

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

        progress_calls: list[tuple[int, int, int]] = []

        async with AsyncMurmrClient(api_key="test_key", base_url=BASE_URL) as client:
            await client.speech.create_long_form(
                input="Hello world",
                voice="voice_abc",
                on_progress=lambda c, t, p: progress_calls.append((c, t, p)),
            )

        assert len(progress_calls) == 1
        current, total, percent = progress_calls[0]
        assert current == 1
        assert total == 1
        assert percent == 100

    @respx.mock
    @pytest.mark.asyncio
    async def test_empty_text_returns_empty_result(self) -> None:
        async with AsyncMurmrClient(api_key="test_key", base_url=BASE_URL) as client:
            result = await client.speech.create_long_form(
                input="   ", voice="voice_abc"
            )
            assert result.total_chunks == 0
            assert result.audio == b""
            assert result.character_count == 0

    @respx.mock
    @pytest.mark.asyncio
    async def test_chunk_failure_raises_chunk_error(self) -> None:
        """Failed chunk should raise MurmrChunkError with context."""
        respx.post(f"{BASE_URL}/v1/audio/speech/stream").mock(
            return_value=httpx.Response(
                500,
                json={"error": {"message": "Internal server error"}},
            )
        )

        async with AsyncMurmrClient(api_key="test_key", base_url=BASE_URL) as client:
            with pytest.raises(MurmrChunkError) as exc_info:
                await client.speech.create_long_form(
                    input="Hello world",
                    voice="voice_abc",
                    max_retries=0,
                )
            err = exc_info.value
            assert err.chunk_index == 0
            assert err.completed_chunks == 0
            assert err.total_chunks == 1


class TestSyncLongForm:
    @respx.mock
    def test_long_form_sync(self) -> None:
        pcm = b"\x05\x06" * 100
        sse_body = make_sse_body(pcm)

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

        with MurmrClient(api_key="test_key", base_url=BASE_URL) as client:
            result = client.speech.create_long_form(
                input="Hello world", voice="voice_abc"
            )
            assert result.total_chunks == 1
            assert result.audio[:4] == b"RIFF"
