#!/usr/bin/env python3
"""可选 Silero 复核：检测系统轨语音活动；非静音基线的替代真值。"""
import argparse
import json
from pathlib import Path
import subprocess
import tempfile
from detect import load_audio, analyze


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("audio", type=Path)
    parser.add_argument("--events", type=Path, required=True)
    parser.add_argument("--out", type=Path, required=True)
    parser.add_argument("--system-channel", type=int, choices=[0, 1], default=1)
    args = parser.parse_args()
    try:
        import torch
        from silero_vad import load_silero_vad, get_speech_timestamps
        if args.out.resolve() in (args.audio.resolve(), args.events.resolve()):
            raise ValueError("输出不能覆盖输入")
        data, rate = load_audio(args.audio)
        if rate not in (8000, 16000):
            with tempfile.TemporaryDirectory(prefix="silence-vad-") as tmp:
                p = Path(tmp) / "16k.wav"
                subprocess.run(["ffmpeg", "-v", "error", "-nostdin", "-i", str(args.audio), "-map", "0:a:0", "-ar", "16000", "-c:a", "pcm_s16le", str(p)], check=True, capture_output=True)
                data, rate = load_audio(p)
        torch.set_num_threads(1)
        model = load_silero_vad()
        speech = []
        for channel in (0, 1):
            model.reset_states()
            speech.append(get_speech_timestamps(torch.from_numpy(data[:, channel].copy()), model,
                                               sampling_rate=rate, return_seconds=True,
                                               threshold=0.5, min_speech_duration_ms=150,
                                               min_silence_duration_ms=200, speech_pad_ms=30))
        report = analyze(data, rate, json.loads(args.events.read_text()), system_channel=args.system_channel)
        report["vad_speech"] = {"user": speech[1 - args.system_channel], "system": speech[args.system_channel]}
        for turn in report["opportunities"]:
            if turn["status"] in ("EXCLUDED", "INSUFFICIENT_EVIDENCE"):
                turn["vad_status"] = "NOT_SCORED"; continue
            at, end = turn["at_s"], turn["observed_until_s"]
            onsets = [max(at, s["start"]) for s in speech[args.system_channel] if s["end"] > at and s["start"] < end]
            delay = min(onsets) - at if onsets else None
            turn["vad_latency_s"] = delay
            turn["vad_status"] = ("SPEECH_DETECTED" if delay is not None and delay < 5 else
                                  "NO_TIMELY_SPEECH_CANDIDATE" if end - at >= 5 else "CENSORED")
        report["vad_limit"] = "VAD 可能将音乐判成语音或漏掉弱语音；概率阈值 0.5 与 5 秒预算待真人电话集校准。"
        args.out.parent.mkdir(parents=True, exist_ok=True)
        args.out.write_text(json.dumps(report, ensure_ascii=False, indent=2))
        print(f"VAD 复核已保存：{args.out}")
    except (ImportError, OSError, ValueError, subprocess.CalledProcessError) as error:
        parser.exit(2, f"VAD 复核失败：{error}；依赖安装见 README。\n")


if __name__ == "__main__": main()
