"""Streaming-TTS chunk reassembly and the near-miss log. Both are pure —
no network, no audio device, no ONNX model."""

import sys
from pathlib import Path

import numpy as np

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from bolt_pet import config as tts_config
from bolt_pet.audio.tts import chunks_to_int16, model_for, voice_for
from bolt_pet.audio.wake_word import NearMissLog


def _pcm(*values):
    return np.array(values, dtype=np.int16).tobytes()


def test_whole_samples_pass_straight_through():
    chunks = list(chunks_to_int16([_pcm(1, 2), _pcm(3, 4)]))
    assert np.concatenate(chunks).tolist() == [1, 2, 3, 4]


def test_a_sample_split_across_two_http_chunks_is_rejoined():
    # The killer bug this exists to prevent: an odd byte at a chunk boundary
    # shifts everything after it by one byte and plays as static.
    raw = _pcm(100, -200, 300, -400)
    chunks = list(chunks_to_int16([raw[:3], raw[3:]]))
    assert np.concatenate(chunks).tolist() == [100, -200, 300, -400]


def test_many_odd_boundaries_in_a_row():
    raw = _pcm(*range(1, 21))
    pieces = [raw[i:i + 3] for i in range(0, len(raw), 3)]  # every boundary odd
    assert np.concatenate(list(chunks_to_int16(pieces))).tolist() == list(range(1, 21))


def test_empty_chunks_are_skipped():
    assert list(chunks_to_int16([b"", b""])) == []


def test_a_dangling_byte_at_the_end_is_dropped_not_played():
    raw = _pcm(7, 8) + b"\x01"
    assert np.concatenate(list(chunks_to_int16([raw]))).tolist() == [7, 8]


# ── wake-word near misses ───────────────────────────────────────────────────

def test_scores_just_under_the_threshold_are_recorded():
    log = NearMissLog(limit=10, margin=0.2)
    assert log.observe(0.45, threshold=0.5, timestamp=1.0) is True
    assert log.entries() == [(1.0, 0.45, 0.5)]


def test_detections_and_background_noise_are_not_near_misses():
    log = NearMissLog(limit=10, margin=0.2)
    assert log.observe(0.90, threshold=0.5, timestamp=1.0) is False  # it fired
    assert log.observe(0.05, threshold=0.5, timestamp=2.0) is False  # just noise
    assert log.entries() == []


def test_peak_tracks_every_score_not_just_near_misses():
    log = NearMissLog(limit=10, margin=0.2)
    log.observe(0.30, threshold=0.5, timestamp=1.0)
    log.observe(0.95, threshold=0.5, timestamp=2.0)
    log.observe(0.10, threshold=0.5, timestamp=3.0)
    assert log.peak == 0.95


def test_the_log_is_bounded():
    log = NearMissLog(limit=3, margin=0.2)
    for i in range(10):
        log.observe(0.45, threshold=0.5, timestamp=float(i))
    assert len(log.entries()) == 3
    assert log.entries()[-1][0] == 9.0


def test_clear_resets_peak_and_entries():
    log = NearMissLog(limit=3, margin=0.2)
    log.observe(0.45, threshold=0.5, timestamp=1.0)
    log.clear()
    assert log.entries() == [] and log.peak == 0.0


# ── voice / model selection (server speak_as) ───────────────────────────────

def test_the_override_voice_wins_over_the_configured_one(monkeypatch):
    monkeypatch.setattr(tts_config, "ELEVENLABS_VOICE_ID", "DEFAULT")
    assert voice_for("VOICE1") == "VOICE1"
    assert voice_for("") == "DEFAULT"
    assert voice_for(None) == "DEFAULT"


def test_english_replies_in_the_default_voice_use_the_default_model(monkeypatch):
    monkeypatch.setattr(tts_config, "ELEVENLABS_MODEL_ID", "eleven_flash_v2")
    monkeypatch.setattr(tts_config, "ELEVENLABS_MULTILINGUAL_MODEL_ID", "eleven_flash_v2_5")
    assert model_for("all good here", None) == "eleven_flash_v2"


def test_a_picked_voice_or_non_english_text_uses_the_multilingual_model(monkeypatch):
    # eleven_flash_v2 is English-only: it would read either of these as
    # mangled phonetic English rather than failing outright.
    monkeypatch.setattr(tts_config, "ELEVENLABS_MODEL_ID", "eleven_flash_v2")
    monkeypatch.setattr(tts_config, "ELEVENLABS_MULTILINGUAL_MODEL_ID", "eleven_flash_v2_5")
    assert model_for("all good here", "VOICE1") == "eleven_flash_v2_5"
    assert model_for("こんにちは", None) == "eleven_flash_v2_5"
