# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""
Teamsbot Feature - Data Models.
Pydantic models for Teams Bot sessions, transcripts, bot responses, and configuration.
"""
from typing import Optional, List, Dict, Any
from pydantic import BaseModel, Field
from enum import Enum
import uuid


# ============================================================================
# Enums
# ============================================================================

class TeamsbotSessionStatus(str, Enum):
    """Status of a Teams Bot session."""
    PENDING = "pending"           # Session created, waiting for bridge
    JOINING = "joining"           # Bridge is joining the meeting
    ACTIVE = "active"             # Bot is in the meeting and processing audio
    LEAVING = "leaving"           # Bot is leaving the meeting
    ENDED = "ended"               # Session completed normally
    ERROR = "error"               # Session ended with an error


class TeamsbotResponseType(str, Enum):
    """Type of bot response delivery."""
    AUDIO = "audio"               # Voice response only
    CHAT = "chat"                 # Chat message only
    BOTH = "both"                 # Voice + chat message


class TeamsbotDetectedIntent(str, Enum):
    """Intent detected by the SPEECH_TEAMS AI handler."""
    ADDRESSED = "addressed"       # Bot was directly addressed
    QUESTION = "question"         # A general question was asked
    PROACTIVE = "proactive"       # Bot has a valuable proactive contribution
    STOP = "stop"                 # User asked the bot to stop/be quiet
    NONE = "none"                 # No action needed


class TeamsbotResponseMode(str, Enum):
    """How the bot should respond."""
    AUTO = "auto"                 # Fully automatic: AI decides when to respond
    MANUAL = "manual"             # User triggers responses manually from UI
    TRANSCRIBE_ONLY = "transcribeOnly"  # Only transcribe, no AI responses


class TeamsbotResponseChannel(str, Enum):
    """Channel for bot responses."""
    VOICE = "voice"               # Bot responds only via voice (TTS)
    CHAT = "chat"                 # Bot responds only via chat message
    BOTH = "both"                 # Bot responds via voice AND chat


class TeamsbotJoinMode(str, Enum):
    """How the bot joins the meeting."""
    SYSTEM_BOT = "systemBot"      # Join with system bot account (backend-managed credentials)
    ANONYMOUS = "anonymous"       # Join as anonymous guest
    USER_ACCOUNT = "userAccount"  # Join with user's own Microsoft account (OAuth)


class TeamsbotTransferMode(str, Enum):
    """How meeting audio/transcript is transferred from bot to gateway."""
    CAPTION = "caption"           # Use Teams live captions (text scraping from DOM)
    AUDIO = "audio"               # Capture meeting audio and stream to gateway for STT
    AUTO = "auto"                 # Automatic: anonymous → audio, authenticated → caption


# ============================================================================
# Database Models (stored in PostgreSQL)
# ============================================================================

class TeamsbotSession(BaseModel):
    """A Teams Bot meeting session."""
    id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Session ID")
    instanceId: str = Field(description="Feature instance ID (FK)")
    mandateId: str = Field(description="Mandate ID (FK)")
    meetingLink: str = Field(description="Teams meeting join link")
    botName: str = Field(default="AI Assistant", description="Display name of the bot in the meeting")
    status: TeamsbotSessionStatus = Field(default=TeamsbotSessionStatus.PENDING, description="Current session status")
    startedAt: Optional[str] = Field(default=None, description="ISO timestamp when session started")
    endedAt: Optional[str] = Field(default=None, description="ISO timestamp when session ended")
    startedByUserId: str = Field(description="User ID who started the session")
    bridgeSessionId: Optional[str] = Field(default=None, description="Session ID on the .NET Media Bridge")
    meetingChatId: Optional[str] = Field(default=None, description="Teams meeting chat ID for Graph API messages")
    sessionContext: Optional[str] = Field(default=None, description="Custom context/knowledge provided by the user for this session")
    summary: Optional[str] = Field(default=None, description="AI-generated meeting summary (after session ends)")
    errorMessage: Optional[str] = Field(default=None, description="Error message if status is ERROR")
    transcriptSegmentCount: int = Field(default=0, description="Number of transcript segments in this session")
    botResponseCount: int = Field(default=0, description="Number of bot responses in this session")
    creationDate: Optional[str] = Field(default=None, description="ISO timestamp of record creation")
    lastModified: Optional[str] = Field(default=None, description="ISO timestamp of last modification")


class TeamsbotTranscript(BaseModel):
    """A single transcript segment from the meeting."""
    id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Transcript segment ID")
    sessionId: str = Field(description="Session ID (FK)")
    speaker: Optional[str] = Field(default=None, description="Speaker name or identifier")
    text: str = Field(description="Transcribed text")
    timestamp: str = Field(description="ISO timestamp of the speech segment")
    confidence: float = Field(default=0.0, ge=0.0, le=1.0, description="STT confidence score")
    language: Optional[str] = Field(default=None, description="Detected language code (e.g., de-DE)")
    isFinal: bool = Field(default=True, description="Whether this is a final or interim result")
    creationDate: Optional[str] = Field(default=None, description="ISO timestamp of record creation")


class TeamsbotBotResponse(BaseModel):
    """A bot response generated during a meeting session."""
    id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Response ID")
    sessionId: str = Field(description="Session ID (FK)")
    responseText: str = Field(description="The bot's response text")
    responseType: TeamsbotResponseType = Field(default=TeamsbotResponseType.BOTH, description="How the response was delivered")
    detectedIntent: TeamsbotDetectedIntent = Field(default=TeamsbotDetectedIntent.NONE, description="What triggered the response")
    reasoning: Optional[str] = Field(default=None, description="AI reasoning for why it responded")
    triggeredByTranscriptId: Optional[str] = Field(default=None, description="Transcript segment that triggered this response")
    modelName: Optional[str] = Field(default=None, description="AI model used for this response")
    processingTime: float = Field(default=0.0, description="Processing time in seconds")
    priceCHF: float = Field(default=0.0, description="Cost of this AI call in CHF")
    timestamp: Optional[str] = Field(default=None, description="ISO timestamp of the response")
    creationDate: Optional[str] = Field(default=None, description="ISO timestamp of record creation")


# ============================================================================
# System Bot Accounts (stored in PostgreSQL, credentials encrypted)
# ============================================================================

class TeamsbotSystemBot(BaseModel):
    """A system bot account for authenticated meeting joins.
    Credentials are stored encrypted in the database, NOT in the UI-visible config.
    Only mandate admins can manage system bots."""
    id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="System bot ID")
    mandateId: str = Field(description="Mandate ID (FK) - bots are scoped to mandates")
    name: str = Field(description="Display name (e.g. 'Nyla Larsson')")
    email: str = Field(description="Microsoft account email")
    encryptedPassword: str = Field(description="Encrypted Microsoft account password")
    isActive: bool = Field(default=True, description="Whether this bot account is active")
    creationDate: Optional[str] = Field(default=None, description="ISO timestamp of creation")
    lastModified: Optional[str] = Field(default=None, description="ISO timestamp of last modification")


# ============================================================================
# Per-User Settings (stored in PostgreSQL, per user per instance)
# ============================================================================

class TeamsbotUserSettings(BaseModel):
    """Per-user settings for the Teams Bot feature.
    Each user has their own settings per feature instance.
    These override the instance-level defaults (TeamsbotConfig)."""
    id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Settings ID")
    userId: str = Field(description="User ID (FK)")
    instanceId: str = Field(description="Feature instance ID (FK)")
    botName: Optional[str] = Field(default=None, description="Bot display name override")
    aiSystemPrompt: Optional[str] = Field(default=None, description="AI system prompt override")
    responseMode: Optional[str] = Field(default=None, description="Response mode override: auto, manual, transcribeOnly")
    responseChannel: Optional[str] = Field(default=None, description="Response channel override: voice, chat, both")
    transferMode: Optional[str] = Field(default=None, description="Transfer mode override: caption, audio, auto")
    language: Optional[str] = Field(default=None, description="Language override (e.g. de-DE)")
    voiceId: Optional[str] = Field(default=None, description="TTS voice ID override")
    triggerIntervalSeconds: Optional[int] = Field(default=None, description="Trigger interval override")
    triggerCooldownSeconds: Optional[int] = Field(default=None, description="Trigger cooldown override")
    contextWindowSegments: Optional[int] = Field(default=None, description="Context window override")
    creationDate: Optional[str] = Field(default=None, description="ISO timestamp of creation")
    lastModified: Optional[str] = Field(default=None, description="ISO timestamp of last modification")


# ============================================================================
# Configuration Model (stored in FeatureInstance.config JSONB -- serves as DEFAULT template)
# ============================================================================

class TeamsbotConfig(BaseModel):
    """Configuration for a Teams Bot feature instance (serves as default template for new users)."""
    botName: str = Field(default="AI Assistant", description="Default bot display name")
    aiSystemPrompt: str = Field(
        default="Du bist ein hilfreicher Meeting-Assistent. Fasse wichtige Punkte zusammen und beantworte Fragen sachlich.",
        description="Custom system prompt for the AI analysis"
    )
    responseMode: TeamsbotResponseMode = Field(default=TeamsbotResponseMode.AUTO, description="How the bot responds")
    responseChannel: TeamsbotResponseChannel = Field(default=TeamsbotResponseChannel.VOICE, description="Channel for bot responses: voice, chat, or both")
    transferMode: TeamsbotTransferMode = Field(default=TeamsbotTransferMode.AUTO, description="How meeting content is captured: caption (Teams captions), audio (stream to gateway STT), or auto")
    language: str = Field(default="de-DE", description="Primary language for STT/TTS")
    voiceId: Optional[str] = Field(default=None, description="Google TTS voice ID (e.g., de-DE-Standard-A)")
    browserBotUrl: Optional[str] = Field(default=None, description="URL of the Browser Bot service. Falls back to TEAMSBOT_BROWSER_BOT_URL env variable if not set per-instance.")
    triggerIntervalSeconds: int = Field(default=10, ge=3, le=60, description="Seconds between periodic AI analysis triggers")
    triggerCooldownSeconds: int = Field(default=3, ge=1, le=30, description="Minimum seconds between AI calls")
    contextWindowSegments: int = Field(default=20, ge=5, le=100, description="Number of transcript segments to include in AI context")

    def _getEffectiveBrowserBotUrl(self) -> Optional[str]:
        """Resolve the effective browser bot URL: per-instance config takes priority, then env variable."""
        if self.browserBotUrl:
            return self.browserBotUrl
        from modules.shared.configuration import APP_CONFIG
        return APP_CONFIG.get("TEAMSBOT_BROWSER_BOT_URL")


# ============================================================================
# API Request/Response Models
# ============================================================================

class TeamsbotStartSessionRequest(BaseModel):
    """Request to start a new Teams Bot session."""
    meetingLink: str = Field(description="Teams meeting join link (e.g., https://teams.microsoft.com/l/meetup-join/...)")
    botName: Optional[str] = Field(default=None, description="Override bot name for this session")
    connectionId: Optional[str] = Field(default=None, description="Microsoft connection ID for Graph API access")
    joinMode: Optional[TeamsbotJoinMode] = Field(default=None, description="How the bot joins: systemBot, anonymous, or userAccount. Defaults to systemBot if credentials configured, else anonymous.")
    sessionContext: Optional[str] = Field(default=None, description="Custom context/knowledge to provide to the bot for this session (e.g. meeting agenda, documents, background info)")


class TeamsbotSessionResponse(BaseModel):
    """Response for session details."""
    session: TeamsbotSession
    transcripts: Optional[List[TeamsbotTranscript]] = Field(default=None, description="Transcript segments (if requested)")
    botResponses: Optional[List[TeamsbotBotResponse]] = Field(default=None, description="Bot responses (if requested)")


class TeamsbotConfigUpdateRequest(BaseModel):
    """Request to update teamsbot configuration."""
    botName: Optional[str] = None
    aiSystemPrompt: Optional[str] = None
    responseMode: Optional[TeamsbotResponseMode] = None
    responseChannel: Optional[TeamsbotResponseChannel] = None
    transferMode: Optional[TeamsbotTransferMode] = None
    language: Optional[str] = None
    voiceId: Optional[str] = None
    browserBotUrl: Optional[str] = None
    triggerIntervalSeconds: Optional[int] = None
    triggerCooldownSeconds: Optional[int] = None
    contextWindowSegments: Optional[int] = None


# ============================================================================
# SPEECH_TEAMS AI Response Model
# ============================================================================

class TeamsbotCommand(BaseModel):
    """A structured command the AI can issue to control Teams meeting actions."""
    action: str = Field(description="Command action: toggleTranscript, sendChat, readAloud, changeLanguage, toggleMic, toggleCamera")
    params: Optional[Dict[str, Any]] = Field(default=None, description="Action-specific parameters")


class SpeechTeamsResponse(BaseModel):
    """Structured response from the SPEECH_TEAMS AI handler."""
    shouldRespond: bool = Field(description="Whether the bot should respond")
    responseText: Optional[str] = Field(default=None, description="The bot's response text (only if shouldRespond=True)")
    reasoning: str = Field(default="", description="Reasoning for the decision (for logging/debug)")
    detectedIntent: str = Field(default="none", description="Detected intent: addressed, question, proactive, stop, none")
    commands: Optional[List[TeamsbotCommand]] = Field(default=None, description="Optional list of commands to execute (e.g. toggle transcript, send chat, change language)")


# ============================================================================
# Bridge Communication Models
# ============================================================================

class BridgeJoinRequest(BaseModel):
    """Request sent to .NET Media Bridge to join a meeting."""
    meetingLink: str = Field(description="Teams meeting join link")
    botName: str = Field(description="Bot display name")
    gatewayCallbackUrl: str = Field(description="Gateway URL for bridge callbacks")
    gatewayWsUrl: str = Field(description="Gateway WebSocket URL for audio streaming")
    sessionId: str = Field(description="Session ID for correlation")
    gatewayBaseUrl: str = Field(description="Base URL of this gateway instance (e.g. https://gateway-prod.poweron-center.net)")


class BridgeStatusResponse(BaseModel):
    """Status response from the .NET Media Bridge."""
    healthy: bool = Field(description="Whether the bridge is healthy")
    activeSessions: int = Field(default=0, description="Number of active meeting sessions")
    sessions: Optional[List[Dict[str, Any]]] = Field(default=None, description="Details of active sessions")
