# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""
Browser Bot Connector - Communication with the Node.js Browser Bot Service.
Handles HTTP and WebSocket communication for meeting join/leave and transcript/audio streaming.

This replaces the old .NET Media Bridge connector with a simpler browser-based approach.
"""

import logging
import aiohttp
import asyncio
from typing import Optional, Dict, Any, Callable, Awaitable

logger = logging.getLogger(__name__)

# Default timeout for bot HTTP calls (60s to allow for Microsoft auth flow)
_BOT_TIMEOUT = aiohttp.ClientTimeout(total=60)


class BrowserBotConnector:
    """Connector to the Node.js Browser Bot service."""

    def __init__(self, botUrl: Optional[str] = None):
        self.botUrl = (botUrl or "").rstrip("/")

    def _isConfigured(self) -> bool:
        """Check if the bot URL is configured."""
        return bool(self.botUrl)

    async def joinMeeting(
        self,
        sessionId: str,
        meetingUrl: str,
        botName: str,
        instanceId: str,
        gatewayWsUrl: str,
        language: str = "de-DE",
        botAccountEmail: Optional[str] = None,
        botAccountPassword: Optional[str] = None,
        transferMode: str = "auto",
    ) -> Dict[str, Any]:
        """
        Send join command to the Browser Bot service.
        
        The bot will:
        1. Launch a browser (headful for auth, headless for anonymous)
        2. If botAccountEmail/Password provided: authenticate with Microsoft first
        3. Navigate to Teams web app and join the meeting
        4. Enable captions/audio capture based on transferMode
        5. Connect back to Gateway via WebSocket using gatewayWsUrl
        
        Args:
            gatewayWsUrl: Full WebSocket URL for the bot to connect back to
            language: BCP-47 language code for captions spoken language
            botAccountEmail: Microsoft account email for authenticated join (None = anonymous)
            botAccountPassword: Microsoft account password
            transferMode: How to capture meeting content: caption, audio, or auto
        
        Returns:
            Dict with 'success' bool and optional 'error' string.
        """
        if not self._isConfigured():
            logger.warning("Browser Bot URL not configured. Simulating join for development.")
            return {
                "success": True,
                "message": "Development mode: Browser Bot not connected"
            }

        payload = {
            "sessionId": sessionId,
            "meetingUrl": meetingUrl,
            "botName": botName,
            "instanceId": instanceId,
            "gatewayWsUrl": gatewayWsUrl,
            "language": language,
            "transferMode": transferMode,
        }

        # Add authenticated join credentials if configured
        if botAccountEmail and botAccountPassword:
            payload["botAccountEmail"] = botAccountEmail
            payload["botAccountPassword"] = botAccountPassword
            logger.info(f"Bot will join authenticated as {botAccountEmail}")

        try:
            async with aiohttp.ClientSession(timeout=_BOT_TIMEOUT) as session:
                async with session.post(f"{self.botUrl}/api/bot", json=payload) as resp:
                    if resp.status == 200:
                        data = await resp.json()
                        return {
                            "success": data.get("success", True),
                        }
                    else:
                        errorText = await resp.text()
                        logger.error(f"Browser Bot join failed: {resp.status} - {errorText}")
                        return {"success": False, "error": f"Bot returned {resp.status}: {errorText}"}

        except aiohttp.ClientError as e:
            logger.error(f"Browser Bot connection error ({type(e).__name__}): {e} | URL: {self.botUrl}/api/bot")
            return {"success": False, "error": f"Bot connection failed ({type(e).__name__}): {str(e)}"}
        except Exception as e:
            logger.error(f"Browser Bot join error ({type(e).__name__}): {e!r} | URL: {self.botUrl}/api/bot")
            return {"success": False, "error": f"{type(e).__name__}: {str(e) or repr(e)}"}

    async def leaveMeeting(self, sessionId: str) -> Dict[str, Any]:
        """Send leave command to the Browser Bot service."""
        if not self._isConfigured():
            logger.warning("Browser Bot URL not configured. Simulating leave for development.")
            return {"success": True, "message": "Development mode: Browser Bot not connected"}

        try:
            async with aiohttp.ClientSession(timeout=_BOT_TIMEOUT) as session:
                async with session.post(f"{self.botUrl}/api/bot/{sessionId}/leave") as resp:
                    if resp.status == 200:
                        return {"success": True}
                    else:
                        errorText = await resp.text()
                        logger.error(f"Browser Bot leave failed: {resp.status} - {errorText}")
                        return {"success": False, "error": f"Bot returned {resp.status}: {errorText}"}

        except Exception as e:
            logger.error(f"Browser Bot leave error: {e}")
            return {"success": False, "error": str(e)}

    async def getStatus(self, sessionId: Optional[str] = None) -> Dict[str, Any]:
        """Get bot health and optionally session status."""
        if not self._isConfigured():
            return {"healthy": False, "message": "Browser Bot URL not configured"}

        try:
            async with aiohttp.ClientSession(timeout=_BOT_TIMEOUT) as session:
                if sessionId:
                    url = f"{self.botUrl}/api/bot/{sessionId}/status"
                else:
                    url = f"{self.botUrl}/health"
                
                async with session.get(url) as resp:
                    if resp.status == 200:
                        data = await resp.json()
                        return {"healthy": True, **data}
                    else:
                        return {"healthy": False, "error": f"Bot returned {resp.status}"}

        except Exception as e:
            return {"healthy": False, "error": str(e)}

    async def sendAudio(
        self,
        sessionId: str,
        audioData: bytes,
        audioFormat: str = "mp3",
    ) -> bool:
        """
        Send TTS audio to the bot for playback in the meeting.
        This is called via the WebSocket connection, not HTTP.
        
        Note: This method is here for reference but actual audio is sent
        via the WebSocket in the route handler.
        """
        # Audio is sent via WebSocket, not HTTP
        # This method is a placeholder for documentation
        logger.debug(f"sendAudio called for session {sessionId} - should use WebSocket")
        return True
