"""Audio utilities: WAV header construction, silence generation, PCM concatenation."""

from __future__ import annotations

import struct

SAMPLE_RATE = 24000
CHANNELS = 1
BITS_PER_SAMPLE = 16
BYTES_PER_SAMPLE = BITS_PER_SAMPLE // 8
WAV_HEADER_SIZE = 44


def create_wav_header(pcm_data_size: int) -> bytes:
    """Create a 44-byte WAV header for raw PCM data.

    Assumes 24kHz, mono, 16-bit signed little-endian PCM.
    """
    byte_rate = SAMPLE_RATE * CHANNELS * BYTES_PER_SAMPLE
    block_align = CHANNELS * BYTES_PER_SAMPLE
    file_size = WAV_HEADER_SIZE + pcm_data_size - 8

    return struct.pack(
        "<4sI4s4sIHHIIHH4sI",
        b"RIFF",
        file_size,
        b"WAVE",
        b"fmt ",
        16,  # PCM chunk size
        1,  # PCM format
        CHANNELS,
        SAMPLE_RATE,
        byte_rate,
        block_align,
        BITS_PER_SAMPLE,
        b"data",
        pcm_data_size,
    )


def generate_silence(duration_ms: int) -> bytes:
    """Generate silence as raw PCM (16-bit, little-endian, all zeros)."""
    num_samples = int((duration_ms / 1000) * SAMPLE_RATE)
    return b"\x00" * (num_samples * BYTES_PER_SAMPLE)


def extract_pcm(wav_buffer: bytes) -> bytes:
    """Extract raw PCM data from a WAV buffer by walking sub-chunks to find 'data'."""
    if len(wav_buffer) < 12 or wav_buffer[:4] != b"RIFF":
        return wav_buffer

    offset = 12
    while offset + 8 <= len(wav_buffer):
        chunk_id = wav_buffer[offset : offset + 4]
        chunk_size = struct.unpack_from("<I", wav_buffer, offset + 4)[0]
        if chunk_id == b"data":
            return wav_buffer[offset + 8 : offset + 8 + chunk_size]
        offset += 8 + chunk_size

    # Fallback: strip standard 44-byte header
    return wav_buffer[WAV_HEADER_SIZE:]


def concatenate_pcm_chunks(
    chunks: list[bytes],
    silence_between_ms: int = 0,
) -> bytes:
    """Concatenate PCM chunks with optional silence, then wrap in a WAV container."""
    if not chunks:
        return b""

    silence = generate_silence(silence_between_ms) if silence_between_ms > 0 else b""
    parts: list[bytes] = []

    for i, chunk in enumerate(chunks):
        parts.append(chunk)
        if silence and i < len(chunks) - 1:
            parts.append(silence)

    total_pcm = b"".join(parts)
    return create_wav_header(len(total_pcm)) + total_pcm


def estimate_duration_ms(wav_buffer: bytes) -> int:
    """Estimate audio duration in milliseconds from a WAV buffer."""
    bytes_per_second = SAMPLE_RATE * CHANNELS * BYTES_PER_SAMPLE
    data_size = max(0, len(wav_buffer) - WAV_HEADER_SIZE)
    return round((data_size / bytes_per_second) * 1000)
