#!/usr/bin/env python3
"""자비스 원격 테스트 — 볼륨 키우고 스피커/마이크/제미나이 확인.

사용:
  source ~/jarvis_env/bin/activate
  python ~/test_jarvis.py              # 전체 테스트 (큰 소리 재생)
  python ~/test_jarvis.py --volume 100 # 볼륨 %
  python ~/test_jarvis.py --speak-only # 스피커만
  python ~/test_jarvis.py --mic-only   # 마이크 레벨만
  python ~/test_jarvis.py --gemini-only
"""

from __future__ import annotations

import argparse
import os
import subprocess
import sys
import time
from ctypes import CFUNCTYPE, c_char_p, c_int, cdll

os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "hide"

ERROR_HANDLER_FUNC = CFUNCTYPE(None, c_char_p, c_int, c_char_p, c_int, c_char_p)


def _py_error_handler(filename, line, function, err, fmt):
    pass


try:
    asound = cdll.LoadLibrary("libasound.so.2")
    asound.snd_lib_error_set_handler(ERROR_HANDLER_FUNC(_py_error_handler))
except Exception:
    pass


def set_volume(percent: int) -> None:
    """시스템 스피커 볼륨을 percent%로 맞춤 (여러 방식 시도)."""
    percent = max(0, min(150, int(percent)))
    print(f"\n[볼륨] {percent}% 로 설정 시도...")

    cmds = [
        ["wpctl", "set-volume", "@DEFAULT_AUDIO_SINK@", f"{percent / 100:.2f}"],
        ["pactl", "set-sink-volume", "@DEFAULT_SINK@", f"{percent}%"],
        ["amixer", "-q", "sset", "Master", f"{min(percent, 100)}%"],
        ["amixer", "-q", "sset", "PCM", f"{min(percent, 100)}%"],
        ["amixer", "-c", "0", "-q", "sset", "Headphone", f"{min(percent, 100)}%"],
    ]
    ok = False
    for cmd in cmds:
        try:
            r = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
            if r.returncode == 0:
                print(f"  OK: {' '.join(cmd)}")
                ok = True
        except (FileNotFoundError, subprocess.TimeoutExpired):
            pass

    # 음소거 해제
    for cmd in (
        ["wpctl", "set-mute", "@DEFAULT_AUDIO_SINK@", "0"],
        ["pactl", "set-sink-mute", "@DEFAULT_SINK@", "0"],
        ["amixer", "-q", "sset", "Master", "unmute"],
    ):
        try:
            subprocess.run(cmd, capture_output=True, timeout=5)
        except (FileNotFoundError, subprocess.TimeoutExpired):
            pass

    if not ok:
        print("  경고: 볼륨 명령을 적용하지 못했습니다. 재생은 시도합니다.")


def show_volume() -> None:
    for cmd in (
        ["wpctl", "get-volume", "@DEFAULT_AUDIO_SINK@"],
        ["pactl", "get-sink-volume", "@DEFAULT_SINK@"],
        ["amixer", "sget", "Master"],
    ):
        try:
            r = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
            if r.returncode == 0 and r.stdout.strip():
                print(f"  현재: {r.stdout.strip().splitlines()[0]}")
                return
        except (FileNotFoundError, subprocess.TimeoutExpired):
            pass


def list_devices() -> None:
    import pyaudio

    pa = pyaudio.PyAudio()
    print("\n[오디오 장치]")
    for i in range(pa.get_device_count()):
        d = pa.get_device_info_by_index(i)
        if d["maxInputChannels"] > 0 or d["maxOutputChannels"] > 0:
            print(
                f"  {i}: in={d['maxInputChannels']} out={d['maxOutputChannels']} "
                f"{d['name']}"
            )
    pa.terminate()


def load_api_key() -> str:
    key = os.environ.get("GOOGLE_API_KEY", "")
    if key and "여기에_" not in key:
        return key
    # jarvis.py 기본값에서 읽기 (이미 키가 들어있는 경우)
    jarvis = os.path.join(os.path.dirname(os.path.abspath(__file__)), "jarvis.py")
    if os.path.isfile(jarvis):
        text = open(jarvis, encoding="utf-8").read()
        import re

        m = re.search(
            r'GOOGLE_API_KEY = os\.environ\.get\(\s*"GOOGLE_API_KEY",\s*"([^"]+)"\s*\)',
            text,
        )
        if not m:
            m = re.search(
                r'os\.environ\.get\(\s*"GOOGLE_API_KEY",\s*\n\s*"([^"]+)"',
                text,
            )
        if m and "여기에_" not in m.group(1):
            return m.group(1)
    raise SystemExit("GOOGLE_API_KEY 를 찾을 수 없습니다. jarvis.py 에 키를 넣으세요.")


def test_speak(volume_pygame: float = 1.0) -> None:
    from gtts import gTTS
    import pygame

    print("\n[스피커] TTS 재생 — 카메라로 들리는지 확인해 보세요.")
    msg = (
        "안녕하세요. 자비스 스피커 테스트입니다. "
        "지금 소리가 크게 나고 있습니다. 하나, 둘, 셋."
    )
    path = "/tmp/jarvis_test_reply.mp3"
    gTTS(text=msg, lang="ko").save(path)

    pygame.mixer.init()
    pygame.mixer.music.set_volume(max(0.0, min(1.0, volume_pygame)))
    pygame.mixer.music.load(path)
    pygame.mixer.music.play()
    while pygame.mixer.music.get_busy():
        time.sleep(0.1)
    pygame.mixer.music.unload()
    print("  재생 완료.")


def test_beep() -> None:
    """네트워크 없이도 스피커 확인용 짧은 비프."""
    print("\n[스피커] 비프음 재생...")
    # sox/aplay 또는 speaker-test
    for cmd in (
        ["speaker-test", "-t", "sine", "-f", "880", "-l", "1", "-c", "2"],
        [
            "bash",
            "-c",
            "ffmpeg -f lavfi -i 'sine=frequency=880:duration=1' -f wav - "
            "| aplay -q 2>/dev/null",
        ],
    ):
        try:
            r = subprocess.run(cmd, capture_output=True, timeout=8)
            if r.returncode == 0:
                print("  비프 완료.")
                return
        except (FileNotFoundError, subprocess.TimeoutExpired):
            pass
    print("  비프 도구 없음 — TTS만 사용합니다.")


def test_mic(seconds: float = 3.0, mic_index: int | None = None) -> None:
    import numpy as np
    import pyaudio

    print(f"\n[마이크] {seconds:.0f}초간 입력 레벨 측정 (밖에 있어도 OK)...")
    pa = pyaudio.PyAudio()

    candidates: list[int | None] = []
    if mic_index is not None:
        candidates.append(mic_index)
    else:
        for i in range(pa.get_device_count()):
            d = pa.get_device_info_by_index(i)
            if d["maxInputChannels"] <= 0:
                continue
            name = d["name"]
            if "pulse" in name or name == "default":
                candidates.append(i)
            elif "USB" in name:
                candidates.append(i)
        candidates.append(None)  # 시스템 기본

    stream = None
    rate = 16000
    used_index: int | None = None
    for idx in candidates:
        for try_rate in (16000, 44100, 48000):
            kwargs = dict(
                format=pyaudio.paInt16,
                channels=1,
                rate=try_rate,
                input=True,
                frames_per_buffer=1024,
            )
            if idx is not None:
                kwargs["input_device_index"] = idx
            try:
                stream = pa.open(**kwargs)
                rate = try_rate
                used_index = idx
                name = (
                    pa.get_device_info_by_index(idx)["name"]
                    if idx is not None
                    else "default"
                )
                print(f"  열림: index={idx} ({name}) rate={rate}")
                break
            except OSError:
                continue
        if stream is not None:
            break

    if stream is None:
        pa.terminate()
        print("  → 마이크를 열 수 없습니다.")
        return

    peaks = []
    n = max(1, int(rate / 1024 * seconds))
    for _ in range(n):
        data = stream.read(1024, exception_on_overflow=False)
        arr = np.frombuffer(data, dtype=np.int16).astype(np.float32)
        peaks.append(float(np.max(np.abs(arr))))
    stream.stop_stream()
    stream.close()
    pa.terminate()

    peak = max(peaks) if peaks else 0
    avg = sum(peaks) / len(peaks) if peaks else 0
    print(f"  peak={peak:.0f} / 32767  avg={avg:.0f} (device={used_index})")
    if peak < 200:
        print("  → 거의 무음. 마이크 연결·권한·MIC_INDEX 확인 필요.")
    elif peak < 2000:
        print("  → 입력은 있으나 작음. 마이크 게인 올려보세요.")
    else:
        print("  → 마이크 입력 정상으로 보입니다.")


def test_gemini() -> None:
    import google.generativeai as genai

    print("\n[제미나이] 짧은 텍스트 질의...")
    key = load_api_key()
    genai.configure(api_key=key)
    model_name = os.environ.get("GEMINI_MODEL", "gemini-2.0-flash")
    model = genai.GenerativeModel(model_name)
    try:
        r = model.generate_content("한 문장으로만 답해: 너는 누구야?")
        print(f"  모델={model_name}")
        print(f"  답: {(r.text or '').strip()}")
    except Exception as e:
        print(f"  실패: {e}")
        print("  → 모델 이름을 gemini-1.5-flash 등으로 바꿔보세요.")


def main() -> None:
    p = argparse.ArgumentParser(description="자비스 원격 테스트")
    p.add_argument("--volume", type=int, default=100, help="시스템 볼륨 %% (기본 100)")
    p.add_argument("--speak-only", action="store_true")
    p.add_argument("--mic-only", action="store_true")
    p.add_argument("--gemini-only", action="store_true")
    p.add_argument("--mic-index", type=int, default=None)
    p.add_argument("--no-beep", action="store_true")
    args = p.parse_args()

    print("=== 자비스 원격 테스트 ===")
    set_volume(args.volume)
    show_volume()

    only = args.speak_only or args.mic_only or args.gemini_only
    if not only:
        list_devices()

    if args.mic_only:
        test_mic(mic_index=args.mic_index)
        return
    if args.gemini_only:
        test_gemini()
        return
    if args.speak_only:
        if not args.no_beep:
            test_beep()
        test_speak(volume_pygame=1.0)
        return

    if not args.no_beep:
        test_beep()
    test_speak(volume_pygame=1.0)
    test_mic(mic_index=args.mic_index)
    test_gemini()
    print("\n=== 끝 ===")
    print("카메라로 TTS/비프가 들렸는지 확인해 주세요.")
    print("다시 크게:  python ~/test_jarvis.py --speak-only --volume 100")


if __name__ == "__main__":
    main()
