"""Tests for _audio module."""

from __future__ import annotations

import struct

from murmr._audio import (
    BYTES_PER_SAMPLE,
    SAMPLE_RATE,
    WAV_HEADER_SIZE,
    concatenate_pcm_chunks,
    create_wav_header,
    estimate_duration_ms,
    extract_pcm,
    generate_silence,
)


class TestCreateWavHeader:
    def test_header_size(self) -> None:
        header = create_wav_header(1000)
        assert len(header) == WAV_HEADER_SIZE

    def test_riff_magic(self) -> None:
        header = create_wav_header(0)
        assert header[:4] == b"RIFF"
        assert header[8:12] == b"WAVE"

    def test_fmt_chunk(self) -> None:
        header = create_wav_header(0)
        assert header[12:16] == b"fmt "
        # PCM format = 1
        fmt_code = struct.unpack_from("<H", header, 20)[0]
        assert fmt_code == 1

    def test_sample_rate_in_header(self) -> None:
        header = create_wav_header(0)
        sr = struct.unpack_from("<I", header, 24)[0]
        assert sr == SAMPLE_RATE

    def test_data_chunk_size(self) -> None:
        pcm_size = 48000
        header = create_wav_header(pcm_size)
        data_size = struct.unpack_from("<I", header, 40)[0]
        assert data_size == pcm_size


class TestGenerateSilence:
    def test_duration_100ms(self) -> None:
        silence = generate_silence(100)
        expected_samples = int(0.1 * SAMPLE_RATE)
        assert len(silence) == expected_samples * BYTES_PER_SAMPLE

    def test_all_zeros(self) -> None:
        silence = generate_silence(50)
        assert all(b == 0 for b in silence)

    def test_zero_duration(self) -> None:
        assert generate_silence(0) == b""


class TestExtractPcm:
    def test_extracts_from_wav(self) -> None:
        pcm = b"\x01\x02" * 100
        wav = create_wav_header(len(pcm)) + pcm
        extracted = extract_pcm(wav)
        assert extracted == pcm

    def test_returns_raw_if_not_wav(self) -> None:
        raw = b"\x01\x02\x03\x04"
        assert extract_pcm(raw) == raw

    def test_handles_short_buffer(self) -> None:
        short = b"RI"
        assert extract_pcm(short) == short


class TestConcatenatePcmChunks:
    def test_empty_list(self) -> None:
        assert concatenate_pcm_chunks([]) == b""

    def test_single_chunk_has_wav_header(self) -> None:
        pcm = b"\x00\x00" * 100
        result = concatenate_pcm_chunks([pcm])
        assert result[:4] == b"RIFF"
        assert len(result) == WAV_HEADER_SIZE + len(pcm)

    def test_two_chunks_without_silence(self) -> None:
        pcm1 = b"\x01\x00" * 50
        pcm2 = b"\x02\x00" * 50
        result = concatenate_pcm_chunks([pcm1, pcm2])
        extracted = extract_pcm(result)
        assert extracted == pcm1 + pcm2

    def test_two_chunks_with_silence(self) -> None:
        pcm1 = b"\x01\x00" * 50
        pcm2 = b"\x02\x00" * 50
        silence_ms = 100
        result = concatenate_pcm_chunks([pcm1, pcm2], silence_between_ms=silence_ms)
        extracted = extract_pcm(result)
        silence_bytes = generate_silence(silence_ms)
        assert extracted == pcm1 + silence_bytes + pcm2


class TestEstimateDurationMs:
    def test_one_second(self) -> None:
        # 24000 samples * 2 bytes = 48000 bytes of PCM
        pcm_size = SAMPLE_RATE * BYTES_PER_SAMPLE
        wav = create_wav_header(pcm_size) + (b"\x00" * pcm_size)
        assert estimate_duration_ms(wav) == 1000

    def test_empty_wav(self) -> None:
        wav = create_wav_header(0)
        assert estimate_duration_ms(wav) == 0
