#!/usr/bin/env python3
# ============================================================
# AI Voice Agent Platform - Customer Deployment Script
# ============================================================
#
# USAGE:
#   1. Set your AGENT_ID below
#   2. Set PLATFORM_URL to your platform's URL
#   3. Run: python run_agent.py
#
# This script connects to your AI Voice Agent Platform and
# handles incoming calls for the specified agent.
#
# Requirements: pip install websockets httpx pyaudio audioop-lts
# ============================================================

import asyncio
import json
import sys
import os
import signal
import logging
from typing import Optional

import websockets
import httpx

# ── CONFIGURATION (Only change these) ─────────────────────────────────────────

AGENT_ID = "AGENT_1001"                         # ← Your Agent ID here
PLATFORM_URL = "http://localhost:8000"           # ← Platform URL
PLATFORM_WS_URL = "ws://localhost:8000"          # ← Platform WebSocket URL

# ── END CONFIGURATION ──────────────────────────────────────────────────────────

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
)
logger = logging.getLogger(__name__)


# ── Agent Loader ───────────────────────────────────────────────────────────────

async def fetch_agent_config(agent_id: str) -> dict:
    """Fetch agent configuration from platform API."""
    async with httpx.AsyncClient() as client:
        try:
            resp = await client.get(f"{PLATFORM_URL}/api/agents/{agent_id}")
            resp.raise_for_status()
            config = resp.json()
            logger.info(f"Loaded agent: {config['agent_name']} ({agent_id})")
            return config
        except httpx.HTTPStatusError as e:
            if e.response.status_code == 404:
                logger.error(f"Agent {agent_id} not found on platform. Check your AGENT_ID.")
            else:
                logger.error(f"Failed to load agent config: {e}")
            sys.exit(1)
        except httpx.ConnectError:
            logger.error(f"Cannot connect to platform at {PLATFORM_URL}. Is it running?")
            sys.exit(1)


# ── VoBiz / SIP Audio Receiver ────────────────────────────────────────────────

class VoBizCallHandler:
    """
    Handles audio bridging between VoBiz SIP calls and the AI platform.

    In production, VoBiz sends audio via WebSocket or RTP.
    This class provides the integration point.

    Replace the simulate_audio_call() method with your actual
    VoBiz/SIP WebSocket connection logic.
    """

    def __init__(self, agent_id: str, platform_ws_url: str):
        self.agent_id = agent_id
        self.platform_ws_url = platform_ws_url
        self._running = False

    async def handle_incoming_call(self, caller_number: Optional[str] = None):
        """
        Main call handler. Connects to platform WebSocket and bridges audio.
        """
        ws_url = f"{self.platform_ws_url}/ws/call/{self.agent_id}"
        logger.info(f"Connecting to platform: {ws_url}")

        try:
            async with websockets.connect(ws_url, ping_interval=30) as platform_ws:
                logger.info("Connected to AI platform")

                # Start concurrent tasks:
                # 1. Receive and relay audio from SIP to platform
                # 2. Receive and relay audio from platform to SIP/speaker

                await asyncio.gather(
                    self._receive_from_platform(platform_ws),
                    self._send_to_platform(platform_ws, caller_number),
                )

        except websockets.ConnectionClosed as e:
            logger.info(f"Platform connection closed: {e.code}")
        except Exception as e:
            logger.error(f"Call handler error: {e}")

    async def _receive_from_platform(self, ws):
        """
        Receive audio/text from AI platform and play/forward to caller.
        In production: forward binary audio to VoBiz/SIP.
        """
        async for message in ws:
            if isinstance(message, bytes):
                # Binary = TTS audio bytes (PCM, 8kHz, 16-bit, mono)
                # TODO: Send this to VoBiz/SIP stream or speaker
                logger.debug(f"Received audio: {len(message)} bytes")
                # Example: await sip_connection.send_audio(message)

            elif isinstance(message, str):
                try:
                    data = json.loads(message)
                    msg_type = data.get("type", "")

                    if msg_type == "call_started":
                        logger.info(f"Call started: {data.get('call_id')}")

                    elif msg_type == "transcript":
                        logger.info(f"User said: {data.get('text')}")

                    elif msg_type == "agent_response_complete":
                        logger.info(f"Agent replied: {data.get('text')}")

                    elif msg_type == "call_ended":
                        logger.info(f"Call ended. Duration: {data.get('duration_seconds')}s")
                        break

                    elif msg_type == "error":
                        logger.error(f"Platform error: {data.get('message')}")

                except json.JSONDecodeError:
                    pass

    async def _send_to_platform(self, ws, caller_number: Optional[str] = None):
        """
        Send audio from SIP/microphone to AI platform.
        In production: receive audio from VoBiz and forward here.
        """
        # ── INTEGRATION POINT ──────────────────────────────────────────────
        # Replace this with your actual VoBiz audio source.
        # VoBiz typically sends audio via:
        #   1. WebSocket audio stream
        #   2. RTP audio packets
        #   3. HTTP chunked audio
        #
        # Example with VoBiz WebSocket:
        # async with websockets.connect(VOBIZ_AUDIO_URL) as vobiz_ws:
        #     async for audio_frame in vobiz_ws:
        #         await ws.send(audio_frame)
        #
        # For testing, we simulate silence (8000 bytes = 0.5s at 8kHz 16-bit):
        # ──────────────────────────────────────────────────────────────────

        logger.info("Audio sender ready. Waiting for SIP audio stream...")

        # Simulate a test call (replace with real audio source)
        await self._simulate_test_call(ws)

    async def _simulate_test_call(self, ws):
        """
        Simulation mode for testing without real SIP audio.
        Reads from stdin as text input.
        """
        logger.info("=" * 50)
        logger.info("SIMULATION MODE - Type messages to test")
        logger.info("Type 'quit' to end the call")
        logger.info("=" * 50)

        # In real deployment, this would be audio stream
        # For testing: text input simulated as transcript
        loop = asyncio.get_event_loop()

        while True:
            try:
                # Non-blocking stdin read
                text = await loop.run_in_executor(None, input, "You: ")

                if text.lower() in ('quit', 'exit', 'bye'):
                    await ws.send(json.dumps({"type": "end_call"}))
                    break

                # Simulate STT by sending text directly via JSON
                # In real mode: send actual audio bytes for Deepgram to process
                # await ws.send(audio_bytes)

            except (EOFError, KeyboardInterrupt):
                await ws.send(json.dumps({"type": "end_call"}))
                break


# ── VoBiz WebSocket Integration (Production Template) ─────────────────────────

class VoBizIntegration:
    """
    Production VoBiz SIP → Platform bridge.

    VoBiz sends audio to this script via WebSocket.
    This script forwards audio to the AI platform and
    returns TTS audio back to VoBiz.

    Configure VoBiz to send audio to: ws://your-script-host:8001/incoming
    """

    def __init__(self, agent_id: str, platform_ws_url: str, listen_port: int = 8001):
        self.agent_id = agent_id
        self.platform_ws_url = platform_ws_url
        self.listen_port = listen_port

    async def start_server(self):
        """Start WebSocket server for VoBiz to connect to."""
        import websockets.server

        logger.info(f"VoBiz bridge listening on port {self.listen_port}")
        logger.info(f"Configure VoBiz to send audio to: ws://localhost:{self.listen_port}/incoming")

        async def handle_vobiz(websocket):
            """Bridge VoBiz audio to AI platform."""
            platform_url = f"{self.platform_ws_url}/ws/call/{self.agent_id}"

            async with websockets.connect(platform_url) as platform_ws:
                async def forward_to_platform():
                    async for audio_frame in websocket:
                        await platform_ws.send(audio_frame)

                async def forward_to_vobiz():
                    async for response in platform_ws:
                        if isinstance(response, bytes):
                            await websocket.send(response)

                await asyncio.gather(forward_to_platform(), forward_to_vobiz())

        async with websockets.server.serve(handle_vobiz, "0.0.0.0", self.listen_port):
            await asyncio.Future()  # Run forever


# ── Main ───────────────────────────────────────────────────────────────────────

async def main():
    logger.info(f"AI Voice Agent Platform - Deployment Script")
    logger.info(f"Agent ID: {AGENT_ID}")
    logger.info(f"Platform: {PLATFORM_URL}")
    logger.info("-" * 50)

    # Verify agent exists
    config = await fetch_agent_config(AGENT_ID)
    logger.info(f"Agent: {config['agent_name']}")
    logger.info(f"Voice: {config['voice']}")
    logger.info(f"Language: {config['language']}")
    logger.info(f"RAG enabled: {config['has_rag']}")
    logger.info("-" * 50)

    # Check for VoBiz bridge mode
    if "--vobiz-bridge" in sys.argv:
        logger.info("Starting VoBiz bridge mode...")
        bridge = VoBizIntegration(AGENT_ID, PLATFORM_WS_URL)
        await bridge.start_server()
    else:
        # Default: direct call handler / test mode
        handler = VoBizCallHandler(AGENT_ID, PLATFORM_WS_URL)
        await handler.handle_incoming_call()


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        logger.info("Agent stopped by user")
