#!/usr/bin/env python3
"""Generate a click-track WAV file at a known BPM.

Example:
  python tests/make_bpm_test_audio.py --bpm 128 --seconds 60 --output tests/audio_128bpm.wav
"""

from __future__ import annotations

import argparse
import math
import struct
import wave


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Generate known-BPM click track")
    parser.add_argument("--bpm", type=float, required=True, help="Target BPM (e.g. 120)")
    parser.add_argument("--seconds", type=float, default=30.0, help="Audio duration in seconds")
    parser.add_argument("--sample-rate", type=int, default=44100, help="Sample rate")
    parser.add_argument("--click-ms", type=float, default=25.0, help="Click length in milliseconds")
    parser.add_argument(
        "--output",
        default="tests/bpm_test.wav",
        help="Output WAV path",
    )
    return parser.parse_args()


def main() -> int:
    args = parse_args()
    if args.bpm <= 0:
        raise SystemExit("--bpm must be > 0")
    if args.seconds <= 0:
        raise SystemExit("--seconds must be > 0")

    sample_rate = int(args.sample_rate)
    total_samples = int(args.seconds * sample_rate)
    beat_interval = 60.0 / float(args.bpm)
    click_samples = max(1, int((args.click_ms / 1000.0) * sample_rate))

    data = [0.0] * total_samples
    beat_index = 0
    t = 0.0
    while t < args.seconds:
        start = int(t * sample_rate)
        # Slight accent every 4 beats to help human counting.
        freq = 1760.0 if beat_index % 4 == 0 else 1320.0
        amp = 0.9 if beat_index % 4 == 0 else 0.6
        for i in range(click_samples):
            idx = start + i
            if idx >= total_samples:
                break
            env = math.exp(-8.0 * (i / click_samples))
            s = amp * env * math.sin((2.0 * math.pi * freq * i) / sample_rate)
            data[idx] += s
        t += beat_interval
        beat_index += 1

    with wave.open(args.output, "wb") as wf:
        wf.setnchannels(1)
        wf.setsampwidth(2)
        wf.setframerate(sample_rate)
        frames = bytearray()
        for s in data:
            clipped = max(-1.0, min(1.0, s))
            frames.extend(struct.pack("<h", int(clipped * 32767)))
        wf.writeframes(bytes(frames))

    print(f"Wrote {args.output} at {args.bpm:.2f} BPM for {args.seconds:.1f}s")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
