"""macOS output policy for streaming TTS.

On macOS, stream_tts_to_speaker must NOT open a sounddevice OutputStream
(PortAudio/CoreAudio init triggers a kTCCServiceMediaLibrary prompt). It
should route audio through the tempfile/afplay fallback instead.
See PR #62601 / #13291.
"""

import queue
import threading

import pytest


class _FakeStreamer:
    """Minimal chunked-streamer stand-in so the OutputStream setup path runs."""

    sample_rate = 24000
    channels = 1

    def stream(self, text):
        return iter([])


def _run_stream(monkeypatch, system_name):
    """Drive stream_tts_to_speaker once with a mock client on *system_name*.

    Returns True if _import_sounddevice was called during the run.
    """
    import tools.tts_tool as tts

    monkeypatch.setattr("tools.tts_tool.platform.system", lambda: system_name)
    monkeypatch.setattr("tools.tts_tool.get_env_value",
                        lambda name, default=None: "fake-key"
                        if name == "ELEVENLABS_API_KEY" else default)
    monkeypatch.setattr("tools.tts_tool._load_tts_config", lambda: {})

    class _FakeTTS:
        def __init__(self, *a, **k):
            self.text_to_speech = self

        def convert(self, *a, **k):
            return iter([])  # no audio chunks needed for setup assertion

    monkeypatch.setattr("tools.tts_tool._import_elevenlabs", lambda: _FakeTTS)
    monkeypatch.setattr(
        "tools.tts_streaming.resolve_streaming_provider",
        lambda cfg, preferred=None: _FakeStreamer(),
    )

    sd_called = {"hit": False}

    def _spy_import_sd():
        sd_called["hit"] = True
        raise AssertionError("sounddevice must not be imported for output on macOS")

    monkeypatch.setattr("tools.tts_tool._import_sounddevice", _spy_import_sd)

    text_queue: queue.Queue = queue.Queue()
    text_queue.put(None)  # end-of-text sentinel: no sentence spoken
    stop_event = threading.Event()
    done_event = threading.Event()

    tts.stream_tts_to_speaker(text_queue, stop_event, done_event)
    assert done_event.is_set()
    return sd_called["hit"]


def test_streaming_tts_skips_sounddevice_on_macos(monkeypatch):
    assert _run_stream(monkeypatch, "Darwin") is False


def test_streaming_tts_uses_sounddevice_off_macos(monkeypatch):
    # Off macOS the OutputStream setup runs; _import_sounddevice raising here
    # is caught by the function's own guard, so the call itself is what we assert.
    called = _run_stream_offmac(monkeypatch)
    assert called is True


def _run_stream_offmac(monkeypatch):
    """Like _run_stream but tolerant of the sounddevice import being attempted."""
    import tools.tts_tool as tts

    monkeypatch.setattr("tools.tts_tool.platform.system", lambda: "Linux")
    monkeypatch.setattr("tools.tts_tool.get_env_value",
                        lambda name, default=None: "fake-key"
                        if name == "ELEVENLABS_API_KEY" else default)
    monkeypatch.setattr("tools.tts_tool._load_tts_config", lambda: {})

    class _FakeTTS:
        def __init__(self, *a, **k):
            self.text_to_speech = self

        def convert(self, *a, **k):
            return iter([])

    monkeypatch.setattr("tools.tts_tool._import_elevenlabs", lambda: _FakeTTS)
    monkeypatch.setattr(
        "tools.tts_streaming.resolve_streaming_provider",
        lambda cfg, preferred=None: _FakeStreamer(),
    )

    sd_called = {"hit": False}

    def _spy_import_sd():
        sd_called["hit"] = True
        raise OSError("no audio device in test")  # handled by the function's guard

    monkeypatch.setattr("tools.tts_tool._import_sounddevice", _spy_import_sd)

    text_queue: queue.Queue = queue.Queue()
    text_queue.put(None)
    stop_event = threading.Event()
    done_event = threading.Event()

    tts.stream_tts_to_speaker(text_queue, stop_event, done_event)
    assert done_event.is_set()
    return sd_called["hit"]
