"""KoboldCpp v1.124: loopback-only Japanese TTS trial (Python 3, stdlib).
Start KoboldCpp with the audio.cpp Qwen3-TTS VoiceDesign GGUF first.
No microphone, voice sample, cloud API, or third-party package is used.
"""
import argparse
import io
import json
from pathlib import Path
import sys
import time
import urllib.error
import urllib.request
import wave


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--port', type=int, default=5017)
    parser.add_argument('--output', type=Path, default=Path('japanese-trial.wav'))
    args = parser.parse_args()
    if not 1 <= args.port <= 65535:
        parser.error('port must be between 1 and 65535')
    if args.output.exists():
        parser.error('output already exists; choose a new filename')
    payload = {
        'input': '本日は、社内研修のご案内です。開始時刻は午後三時です。',
        'voice': 'instruct',
        'language': 'Japanese',
        'instructions': 'A calm, clear adult voice speaking Japanese at a natural pace.',
        'seed': 42,
        'response_format': 'wav',
    }
    request = urllib.request.Request(
        f'http://127.0.0.1:{args.port}/v1/audio/speech',
        data=json.dumps(payload, ensure_ascii=False).encode('utf-8'),
        headers={'Content-Type': 'application/json'},
    )
    # Ignore proxy environment variables: this trial connects only to loopback.
    opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
    started = time.monotonic()
    try:
        with opener.open(request, timeout=600) as response:
            raw = response.read()
        if len(raw) < 44 or raw[:4] != b'RIFF' or raw[8:12] != b'WAVE':
            raise ValueError('no WAV audio returned; check the loaded TTS model and server log')
        with wave.open(io.BytesIO(raw), 'rb') as wav:
            frames, rate = wav.getnframes(), wav.getframerate()
            if frames <= 0 or rate <= 0:
                raise ValueError('empty WAV audio')
            expected_bytes = frames * wav.getnchannels() * wav.getsampwidth()
            if len(wav.readframes(frames)) != expected_bytes:
                raise ValueError('truncated WAV audio')
            seconds = frames / rate
        # Exclusive creation prevents overwriting an existing recording.
        with args.output.open('xb') as target:
            target.write(raw)
    except (urllib.error.URLError, OSError, ValueError, wave.Error, EOFError) as exc:
        print(f'Trial failed: {exc}', file=sys.stderr)
        return 1
    print(json.dumps({'output': str(args.output), 'audioSeconds': round(seconds, 3),
                      'elapsedSeconds': round(time.monotonic() - started, 3),
                      'bytes': len(raw)}, ensure_ascii=False))
    print('WAV received. Listen to it: this does not certify pronunciation quality.')
    return 0


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