#!/usr/bin/env python3
"""Record 5s from the USB Speaker Phone with a live RMS meter, save out.wav, play it back.

Pass criteria (per spec):
- Playback is recognisable as what was said into the mic.
- Level meter visibly responds to speech and to silence.
- No clipping, dropouts, or sustained PortAudio under/overrun warnings.
"""
import os
os.environ.setdefault("PA_ALSA_PLUGHW", "1")

import sys
import time

import numpy as np
import sounddevice as sd
import soundfile as sf

SAMPLE_RATE = 16_000
DURATION_S = 5
CHANNELS = 1
DTYPE = "int16"
OUT_WAV = "out.wav"


def find_usb_device() -> int:
    for idx, dev in enumerate(sd.query_devices()):
        name = dev["name"].lower()
        if "usb" in name and dev["max_input_channels"] >= 1:
            return idx
    print("USB audio device not found. Devices:", file=sys.stderr)
    print(sd.query_devices(), file=sys.stderr)
    sys.exit(1)


def rms(int16_buf: np.ndarray) -> float:
    if int16_buf.size == 0:
        return 0.0
    x = int16_buf.astype(np.float32) / 32768.0
    return float(np.sqrt((x * x).mean() + 1e-12))


def record(device: int) -> np.ndarray:
    print(f"Recording {DURATION_S}s from device {device} ({sd.query_devices(device)['name']!r})")
    total_frames = SAMPLE_RATE * DURATION_S
    buf = np.zeros((total_frames, CHANNELS), dtype=DTYPE)
    frames_written = 0
    start = time.monotonic()

    def callback(indata, frames, time_info, status):
        nonlocal frames_written
        if status:
            print(f"\n[status] {status}", file=sys.stderr)
        end = min(frames_written + frames, total_frames)
        buf[frames_written:end] = indata[: end - frames_written]
        frames_written = end

    with sd.InputStream(
        samplerate=SAMPLE_RATE,
        channels=CHANNELS,
        dtype=DTYPE,
        device=device,
        blocksize=1024,
        callback=callback,
    ):
        while frames_written < total_frames:
            elapsed = time.monotonic() - start
            tail = buf[max(0, frames_written - 1600) : frames_written]
            level = rms(tail)
            bar = "#" * int(level * 60)
            print(f"\r{elapsed:4.1f}s |{bar:<60}|", end="", flush=True)
            time.sleep(0.05)
    print()
    return buf


def play(device: int, audio: np.ndarray) -> None:
    print(f"Playing back through device {device}")
    sd.play(audio, samplerate=SAMPLE_RATE, device=device)
    sd.wait()


def main() -> int:
    device = find_usb_device()
    audio = record(device)
    sf.write(OUT_WAV, audio, SAMPLE_RATE)
    print(f"Wrote {OUT_WAV} ({audio.shape[0]} frames @ {SAMPLE_RATE} Hz)")
    play(device, audio)
    return 0


if __name__ == "__main__":
    sys.exit(main())
