"""Unit tests for audio utilities."""

import struct
import tempfile
import wave

import pytest

from dtelecom_stt.audio import BYTES_PER_SECOND, load_wav, silence
from dtelecom_stt.exceptions import AudioFormatError


def _make_wav(path: str, sample_rate=16000, channels=1, sample_width=2, duration=1.0):
    """Create a test WAV file with a sine wave."""
    import math

    n_frames = int(sample_rate * duration)
    with wave.open(path, "wb") as wf:
        wf.setnchannels(channels)
        wf.setsampwidth(sample_width)
        wf.setframerate(sample_rate)
        for i in range(n_frames):
            # Simple 440Hz sine wave
            value = int(32767 * 0.5 * math.sin(2 * math.pi * 440 * i / sample_rate))
            if channels == 1 and sample_width == 2:
                wf.writeframes(struct.pack("<h", value))
            elif channels == 2 and sample_width == 2:
                wf.writeframes(struct.pack("<hh", value, value))
            elif channels == 1 and sample_width == 1:
                # 8-bit unsigned
                wf.writeframes(struct.pack("B", (value >> 8) + 128))


class TestLoadWav:
    def test_valid_wav(self, tmp_path):
        path = str(tmp_path / "test.wav")
        _make_wav(path, sample_rate=16000, channels=1, sample_width=2, duration=0.5)

        pcm_data, sr, ch, sw = load_wav(path)
        assert sr == 16000
        assert ch == 1
        assert sw == 2
        expected_bytes = int(16000 * 2 * 0.5)
        assert len(pcm_data) == expected_bytes

    def test_wrong_sample_rate(self, tmp_path):
        path = str(tmp_path / "test_44k.wav")
        _make_wav(path, sample_rate=44100)

        with pytest.raises(AudioFormatError, match="16000Hz"):
            load_wav(path)

    def test_wrong_channels(self, tmp_path):
        path = str(tmp_path / "test_stereo.wav")
        _make_wav(path, channels=2)

        with pytest.raises(AudioFormatError, match="mono"):
            load_wav(path)

    def test_wrong_bit_depth(self, tmp_path):
        path = str(tmp_path / "test_8bit.wav")
        _make_wav(path, sample_width=1)

        with pytest.raises(AudioFormatError, match="16-bit"):
            load_wav(path)

    def test_file_not_found(self):
        with pytest.raises(AudioFormatError, match="not found"):
            load_wav("/nonexistent/file.wav")

    def test_not_a_wav(self, tmp_path):
        path = str(tmp_path / "not_a_wav.wav")
        with open(path, "wb") as f:
            f.write(b"this is not a wav file")

        with pytest.raises(AudioFormatError, match="Cannot read"):
            load_wav(path)


class TestSilence:
    def test_silence_length(self):
        s = silence(1.0)
        assert len(s) == BYTES_PER_SECOND

    def test_silence_zeros(self):
        s = silence(0.1)
        assert all(b == 0 for b in s)

    def test_silence_fractional(self):
        s = silence(0.02)  # 20ms
        expected = int(16000 * 2 * 0.02)
        assert len(s) == expected
