import os
import time
import logging
import requests
import speech_recognition as sr
import pygame
import io
from ctypes import *

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

# ALSA 에러 로그 숨기기
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
c_error_handler = ERROR_HANDLER_FUNC(py_error_handler)
try:
    asound = cdll.LoadLibrary('libasound.so.2')
    asound.snd_lib_error_set_handler(c_error_handler)
except:
    pass

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
logger = logging.getLogger(__name__)

# ─── 환경 설정 ───────────────────────────────────────────────
VM_IP        = "192.168.0.149"
OPENCLAW_PORT = 18789

# 오픈클로 Gateway RPC 엔드포인트
# chat.send: 텍스트 메시지 전송
CHAT_URL     = f"http://{VM_IP}:{OPENCLAW_PORT}/api/v1/chat/completions"
# 오디오 첨부 전송 (media.audio enabled 시 Gemini가 STT 처리)
AUDIO_URL    = f"http://{VM_IP}:{OPENCLAW_PORT}/v1/audio/transcriptions"

# TTS: openedai-speech 또는 오픈클로 내장 TTS
TTS_URL      = f"http://{VM_IP}:{OPENCLAW_PORT}/v1/audio/speech"

WAKE_WORD    = "애미나이"
MIC_INDEX    = 3
# ─────────────────────────────────────────────────────────────

pygame.mixer.init()

def send_audio_to_openclaw(audio_data) -> str:
    """
    WAV를 오픈클로에 전송.
    media.audio.enabled + google provider 설정 시
    오픈클로가 Gemini로 STT 후 LLM 답변까지 처리.
    반환값: 오픈클로 텍스트 응답
    """
    try:
        wav_bytes = audio_data.get_wav_data()
        files = {
            'audio': ('cmd.wav', io.BytesIO(wav_bytes), 'audio/wav')
        }
        logger.info("오픈클로로 오디오 전송 중...")
        resp = requests.post(AUDIO_URL, files=files, timeout=30)
        resp.raise_for_status()
        logger.info(f"응답 코드: {resp.status_code}")

        # 오픈클로 응답이 JSON인 경우
        try:
            data = resp.json()
            # 오픈클로 응답 구조에 따라 키 조정
            return data.get("reply") or data.get("message") or data.get("text") or str(data)
        except Exception:
            # 텍스트 응답인 경우
            return resp.text

    except requests.exceptions.ConnectionError:
        logger.error(f"연결 실패 - {VM_IP}:{OPENCLAW_PORT} 확인 필요")
        return ""
    except Exception as e:
        logger.error(f"오디오 전송 실패: {e}")
        return ""

def send_text_to_openclaw(text: str) -> str:
    """텍스트를 오픈클로 chat API로 전송 (오디오 전송 안 될 때 폴백)"""
    try:
        payload = {"message": text}
        resp = requests.post(CHAT_URL, json=payload, timeout=30)
        resp.raise_for_status()
        data = resp.json()
        return data.get("reply") or data.get("message") or data.get("text") or str(data)
    except Exception as e:
        logger.error(f"텍스트 전송 실패: {e}")
        return ""

def speak_edge_tts(text: str):
    """
    오픈클로 내장 TTS (Edge TTS, API 키 불필요) 사용.
    안 되면 Google gTTS로 폴백.
    """
    try:
        payload = {
            "model": "tts-1",
            "input": text,
            "voice": "alloy",
            "response_format": "mp3"
        }
        resp = requests.post(TTS_URL, json=payload, timeout=15)
        if resp.status_code == 200:
            audio_buf = io.BytesIO(resp.content)
            pygame.mixer.music.load(audio_buf, "mp3")
            pygame.mixer.music.play()
            while pygame.mixer.music.get_busy():
                time.sleep(0.1)
            logger.info("TTS 재생 완료")
            return
    except Exception as e:
        logger.warning(f"오픈클로 TTS 실패, gTTS 시도: {e}")

    # gTTS 폴백 (로컬 설치: pip install gtts)
    try:
        from gtts import gTTS
        tts = gTTS(text=text, lang='ko')
        tts.save("/tmp/reply.mp3")
        pygame.mixer.music.load("/tmp/reply.mp3")
        pygame.mixer.music.play()
        while pygame.mixer.music.get_busy():
            time.sleep(0.1)
    except Exception as e:
        logger.error(f"gTTS도 실패: {e}")

def main():
    recognizer = sr.Recognizer()
    recognizer.energy_threshold = 400
    recognizer.pause_threshold = 1.0

    # 마이크 목록 출력
    mics = sr.Microphone.list_microphone_names()
    logger.info(f"사용 가능한 마이크 ({len(mics)}개):")
    for i, mic in enumerate(mics):
        logger.info(f"  {i}: {mic}")

    # 안전하게 마이크 열기
    mic = None
    for test_index in [MIC_INDEX, 0, 1, 2, 3]:  # 여러 인덱스 시도
        try:
            logger.info(f"마이크 {test_index} 테스트 중...")
            mic = sr.Microphone(device_index=test_index)
            with mic as source:
                test_audio = recognizer.listen(source, timeout=1, phrase_time_limit=1)
            logger.info(f"✅ 마이크 {test_index} 성공!")
            break
        except Exception as e:
            logger.warning(f"❌ 마이크 {test_index} 실패: {e}")
            continue

    if mic is None:
        logger.error("사용 가능한 마이크를 찾을 수 없습니다. 프로그램 종료.")
        return

    # 소음 보정 (실패해도 무시)
    try:
        with mic as source:
            recognizer.adjust_for_ambient_noise(source, duration=1)
        logger.info(f"energy_threshold: {recognizer.energy_threshold:.0f}")
    except Exception as e:
        logger.warning(f"소음 보정 실패 (무시): {e}")

    logger.info(f"==== '{WAKE_WORD}' 대기 중 ====")

    while True:
        try:
            with mic as source:
                logger.debug("마이크 듣는 중...")
                audio = recognizer.listen(source, timeout=None, phrase_time_limit=3)

            try:
                text = recognizer.recognize_google(audio, language='ko-KR')
                logger.info(f"🎤 인식: '{text}'")

                if WAKE_WORD in text:
                    logger.info("🔔 호출어 감지!")
                    speak_edge_tts("네, 말씀하세요")

                    # 명령어 녹음
                    with mic as source:
                        logger.info("명령 녹음 중...")
                        cmd_audio = recognizer.listen(source, timeout=5, phrase_time_limit=10)

                    # 오픈클로 전송
                    reply = send_audio_to_openclaw(cmd_audio)
                    if reply:
                        speak_edge_tts(reply)
                    else:
                        speak_edge_tts("응답을 받지 못했어요.")

            except sr.UnknownValueError:
                pass  # 조용히 무시
            except sr.RequestError as e:
                logger.error(f"Google STT 오류: {e}")

        except sr.WaitTimeoutError:
            pass  # 타임아웃 무시
        except Exception as e:
            logger.error(f"루프 오류: {e}")
            time.sleep(1)


if __name__ == "__main__":
    main()
