import pytest
import asyncio
from unittest.mock import AsyncMock, MagicMock, patch
from pathlib import Path
from types import SimpleNamespace
from navig.voice.pipeline import VoicePipeline
from navig.voice.session_manager import VoiceSessionManager

@pytest.fixture
def pipeline():
    return VoicePipeline()

@pytest.mark.asyncio
async def test_process_audio_file_full(pipeline, monkeypatch):
    # Mock STT
    class MockSTTResult:
        success = True
        text = "Hello transcript"
        latency_ms = 100
        error = None
        
    class MockSTT:
        def __init__(self, *args, **kwargs): pass
        async def transcribe(self, *args, **kwargs):
            return MockSTTResult()
            
    monkeypatch.setattr("navig.voice.stt.STT", MockSTT)
    monkeypatch.setattr("navig.voice.stt.STTConfig", MagicMock())

    # Mock Router for LLM
    class MockRouter:
        async def run(self, req):
            return "Router response", None
    
    monkeypatch.setattr("navig.routing.router.get_router", lambda: MockRouter())

    # Mock TTS
    class MockTTSResult:
        success = True
        audio_path = "/tmp/audio.mp3"
        error = None

    class MockTTS:
        def __init__(self, *args, **kwargs): pass
        async def synthesize(self, text):
            return MockTTSResult()

    monkeypatch.setattr("navig.voice.tts.TTS", MockTTS)
    monkeypatch.setattr("navig.voice.tts.TTSConfig", MagicMock())

    res = await pipeline.process_audio_file(Path("test.wav"), is_voice=True)
    
    assert res.transcript == "Hello transcript"
    assert res.response_text == "Router response"
    assert res.audio_path == "/tmp/audio.mp3"
    assert res.duration_ms is not None

@pytest.mark.asyncio
async def test_process_audio_file_stt_failure(pipeline, monkeypatch):
    class MockSTTResultFail:
        success = False
        text = None
        error = "Network issue"
        
    monkeypatch.setattr("navig.voice.stt.STT", lambda **kw: SimpleNamespace(transcribe=AsyncMock(return_value=MockSTTResultFail())))
    monkeypatch.setattr("navig.voice.stt.STTConfig", MagicMock())

    res = await pipeline.process_audio_file(Path("test.wav"))
    assert res.transcript is None
    assert res.response_text is None
    assert res.error is None  # Error logged but graceful degradation returned

@pytest.mark.asyncio
async def test_process_audio_file_llm_routing_error(pipeline, monkeypatch):
    monkeypatch.setattr("navig.voice.stt.STT", lambda **kw: SimpleNamespace(transcribe=AsyncMock(return_value=SimpleNamespace(success=True, text="Hi", latency_ms=10, error=None))))
    monkeypatch.setattr("navig.voice.stt.STTConfig", MagicMock())
    
    class MockFailRouter:
        async def run(self, req):
            raise ValueError("LLM is down")
            
    monkeypatch.setattr("navig.routing.router.get_router", lambda: MockFailRouter())
    monkeypatch.setattr("navig.voice.tts.TTS", lambda **kw: SimpleNamespace(synthesize=AsyncMock(return_value=SimpleNamespace(success=True, audio_path="/x", error=None))))
    monkeypatch.setattr("navig.voice.tts.TTSConfig", MagicMock())

    res = await pipeline.process_audio_file(Path("test.wav"))
    assert res.transcript == "Hi"
    assert "I'm sorry" in res.response_text
    assert res.audio_path == "/x"

@pytest.mark.asyncio
async def test_lifecycle(pipeline, monkeypatch):
    mock_start = AsyncMock()
    mock_stop = AsyncMock()
    
    class DummyWakeEngine:
        def __init__(self, *args, **kwargs): pass
        start = mock_start
        stop = mock_stop
        
    class DummySessionMgr:
        def __init__(self, *args, **kwargs): pass
        start = AsyncMock()
        stop = AsyncMock()
        
    monkeypatch.setattr("navig.voice.wake_word.WakeWordEngine", DummyWakeEngine)
    monkeypatch.setattr("navig.voice.wake_word.WakeWordConfig", MagicMock())
    monkeypatch.setattr("navig.voice.session_manager.VoiceSessionManager", DummySessionMgr)
    monkeypatch.setattr("navig.voice.session_manager.SessionConfig", MagicMock())
    monkeypatch.setattr("navig.voice.streaming_stt.StreamingSTT", lambda **kw: None)
    monkeypatch.setattr("navig.voice.streaming_stt.StreamingSTTConfig", MagicMock())

    await pipeline.start()
    assert mock_start.await_count == 1
    
    await pipeline.stop()
    assert mock_stop.await_count == 1

def test_get_pipeline():
    from navig.voice.pipeline import get_pipeline, _pipeline
    p1 = get_pipeline()
    p2 = get_pipeline()
    assert p1 is p2
