"""END 尾包发送异常和上传硬过期的资源释放回归。"""
import asyncio
from types import SimpleNamespace

import pytest
from device_gateway.voice import VoiceManager
from test_voice_settings import FakeAgent, FakeSTT, start, chunk


@pytest.mark.parametrize('action', ['expiry', 'cancel', 'end_transport_failure'])
def test_pcm_cleanup_and_terminal_transport_failure(action):
    async def run():
        device = SimpleNamespace(device_id='d', connection_id='c')
        adapter = FakeAgent('pi', '/tmp')
        events = []

        class Provider(FakeSTT):
            calls = 0

            async def transcribe(self, audio):
                self.calls += 1
                return await super().transcribe(audio)

        provider = Provider()
        broken = [False]

        async def send(d, event):
            if broken[0]:
                raise ConnectionError('tail packet lost')
            events.append(event)

        manager = VoiceManager(provider, lambda _: 'agent:pi:s', lambda _: adapter,
                               send, lambda _: True, lambda: False)
        try:
            await manager.handle(device, start())
            await manager.handle(device, chunk())
            req = manager.current_for(device)
            assert req.pcm
            if action == 'expiry':
                manager.suspend(device)
                await manager.expire(device, req)
            elif action == 'cancel':
                await manager.handle(device, dict(type='voice_cancel', voice_id='v'))
            else:
                broken[0] = True
                await manager.handle(device, dict(type='voice_end', voice_id='v', total_bytes=3528))
            await asyncio.gather(*list(manager.tasks))
            assert not req.pcm and not adapter.inputs
            broken[0] = False
            await manager.handle(device, dict(type='voice_status', voice_id='v'))
            await manager.handle(device, dict(type='voice_end', voice_id='v'))
            if action == 'end_transport_failure':
                assert provider.calls == 1 and req.state == 'AWAITING_CONFIRMATION'
                assert events[-2]['type'] == 'voice_end_ack'
                assert events[-1]['type'] == 'voice_transcript'
            else:
                assert provider.calls == 0 and not manager.requests
                assert not any(e['type'] == 'voice_end_ack' for e in events)
        finally:
            await manager.close()
    asyncio.run(run())
