"""
NEO VOICE ASSISTANT — Jarvis-style voice interface to the REAL Neo agent
Wake word: "Hey Neo" → Neo responds "Peace God" → continuous conversation
Routes through OpenClaw agent (same brain as Telegram) with full MCP tools,
MemPalace, memory-lancedb, Shopify — everything.

Usage:
  python3 neo-voice-assistant.py
"""

import os
import sys
import json
import time
import tempfile
import subprocess
import re
import speech_recognition as sr
import httpx

# ── Config ──
def _env(name, default=None):
    """Env first, then the gitignored .env next to this script. Never hardcode; fail loud."""
    val = os.environ.get(name)
    if val:
        return val
    env_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env")
    if os.path.exists(env_path):
        with open(env_path) as f:
            for line in f:
                line = line.strip()
                if line.startswith(name + "="):
                    return line.split("=", 1)[1].strip()
    return default


ELEVENLABS_KEY = _env("ELEVENLABS_API_KEY")
VOICE_ID = _env("ELEVENLABS_VOICE_ID", "YjyOkKA67HmAqGFoFWMd")  # 19Keys cloned voice
TELEGRAM_TOKEN = _env("NEO_TELEGRAM_BOT_TOKEN")
TELEGRAM_CHAT = _env("NEO_TELEGRAM_CHAT_ID")

if not (ELEVENLABS_KEY and TELEGRAM_TOKEN and TELEGRAM_CHAT):
    sys.exit(
        "ERROR: missing ELEVENLABS_API_KEY / NEO_TELEGRAM_BOT_TOKEN / NEO_TELEGRAM_CHAT_ID "
        "(set in env or the .env at repo root — see .env.example)."
    )

WAKE_WORDS = ["hey neo", "hey neil", "a neo", "hey new", "henio", "hey nio"]
EXIT_WORDS = ["peace god", "peace gods", "goodbye", "shut down", "go to sleep"]


def speak(text):
    """Speak using 19Keys' cloned voice via ElevenLabs."""
    if not text or len(text.strip()) < 2:
        return

    clean = text.replace("**", "").replace("*", "").replace("#", "").replace("`", "")
    clean = re.sub(r'\[.*?\]\(.*?\)', '', clean)  # Remove markdown links
    clean = clean.replace("\n- ", ". ").replace("\n", ". ").strip()
    clean = clean[:800]  # Cap for TTS

    print(f"\n  🔊 NEO: {text}\n")

    try:
        resp = httpx.post(
            f"https://api.elevenlabs.io/v1/text-to-speech/{VOICE_ID}/stream",
            headers={"xi-api-key": ELEVENLABS_KEY, "Content-Type": "application/json"},
            json={
                "text": clean,
                "model_id": "eleven_turbo_v2_5",
                "voice_settings": {"stability": 0.5, "similarity_boost": 0.85, "style": 0.3},
            },
            timeout=30,
        )
        if resp.status_code == 200:
            with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as f:
                f.write(resp.content)
                temp_path = f.name
            subprocess.run(["afplay", temp_path], check=True)
            os.unlink(temp_path)
        else:
            subprocess.run(["say", "-v", "Alex", clean[:200]])
    except Exception as e:
        print(f"  [TTS Error: {e}]")
        subprocess.run(["say", "-v", "Alex", clean[:200]])


def ask_neo_via_openclaw(message):
    """Send message through the REAL Neo OpenClaw agent and get response."""

    # Send to Neo via Telegram (the actual Neo agent picks it up)
    try:
        send_resp = httpx.post(
            f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage",
            json={"chat_id": TELEGRAM_CHAT, "text": f"[VOICE] {message}"},
            timeout=10,
        )
        sent_msg_id = send_resp.json().get("result", {}).get("message_id", 0)
    except Exception as e:
        return f"Couldn't reach Neo. {e}"

    # Poll for Neo's response (he replies in the same chat)
    print("  ⏳ [Neo is thinking...]")
    start = time.time()
    last_update_id = 0

    for attempt in range(20):  # Up to 40 seconds
        time.sleep(2)

        try:
            resp = httpx.get(
                f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/getUpdates",
                params={"offset": -3, "limit": 3, "timeout": 1},
                timeout=10,
            )
            updates = resp.json().get("result", [])

            for update in reversed(updates):
                msg = update.get("message", {})
                # Look for bot replies after our sent message
                msg_id = msg.get("message_id", 0)
                from_bot = msg.get("from", {}).get("is_bot", False)
                text = msg.get("text", "")

                if from_bot and msg_id > sent_msg_id and text:
                    elapsed = time.time() - start
                    print(f"  ✓ [Neo responded in {elapsed:.1f}s]")
                    return text

        except Exception:
            pass

        if time.time() - start > 40:
            break

    return "I'm still processing that. Check Telegram for my full response."


def listen(recognizer, mic, timeout=None, phrase_limit=10):
    """Listen for speech and return transcription."""
    try:
        with mic as source:
            audio = recognizer.listen(source, timeout=timeout, phrase_time_limit=phrase_limit)
        text = recognizer.recognize_google(audio).lower()
        return text
    except sr.WaitTimeoutError:
        return None
    except sr.UnknownValueError:
        return None
    except Exception as e:
        print(f"  [Listen error: {e}]")
        return None


def main():
    recognizer = sr.Recognizer()
    recognizer.energy_threshold = 300
    recognizer.dynamic_energy_threshold = True

    # Find MacBook Pro microphone
    mic_index = None
    for i, name in enumerate(sr.Microphone.list_microphone_names()):
        if "MacBook" in name or "Built-in" in name:
            mic_index = i
            break
    mic = sr.Microphone(device_index=mic_index) if mic_index is not None else sr.Microphone()

    print(f"\n  [Using mic: {sr.Microphone.list_microphone_names()[mic_index or 0]}]")
    print("\n  [Calibrating...]")
    with mic as source:
        recognizer.adjust_for_ambient_noise(source, duration=2)

    print("\n" + "=" * 50)
    print("  ⚡ NEO VOICE ASSISTANT ⚡")
    print("  Powered by OpenClaw Agent (full Neo brain)")
    print("  Say 'Hey Neo' to activate")
    print("  Continuous conversation until 'Peace God'")
    print("=" * 50 + "\n")

    while True:
        # ── PHASE 1: Wait for wake word ──
        print("  👂 [Waiting for 'Hey Neo'...]")
        text = listen(recognizer, mic, timeout=None, phrase_limit=4)

        if text is None:
            continue

        if not any(wake in text for wake in WAKE_WORDS):
            continue

        # ── PHASE 2: Activated ──
        print("\n  ⚡ [ACTIVATED]")
        speak("Peace god. What's on your mind?")

        # ── PHASE 3: Continuous conversation through real Neo ──
        silence_count = 0
        while True:
            print("  🎤 [Listening...]")
            text = listen(recognizer, mic, timeout=8, phrase_limit=20)

            if text is None:
                silence_count += 1
                if silence_count >= 3:
                    speak("I'm here when you need me.")
                    break
                continue

            silence_count = 0
            print(f"\n  🗣  YOU: {text}")

            # Check for exit
            if any(exit_word in text for exit_word in EXIT_WORDS):
                speak("Peace god. The kingdom stays running.")
                break

            # Check for re-engage
            if any(wake in text for wake in WAKE_WORDS):
                speak("I'm right here. What you need?")
                continue

            # ── Route through REAL Neo agent via Telegram ──
            response = ask_neo_via_openclaw(text)
            speak(response)

        print("\n  💤 [Session ended — back to wake word mode]\n")


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\n\n  Neo Voice Assistant offline. Peace god.\n")
