//! Event types for the LiveSpeech SDK

use serde::{Deserialize, Serialize};

/// Disconnect reason
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DisconnectReason {
    /// Normal disconnection (client initiated)
    Normal,
    /// Error occurred
    Error,
    /// Connection timeout
    Timeout,
    /// Server closed connection
    ServerClose,
    /// Max reconnection attempts reached
    ReconnectFailed,
}

/// Error codes
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ErrorCode {
    ConnectionFailed,
    ConnectionTimeout,
    AuthenticationFailed,
    SessionError,
    AudioError,
    StreamingError,
    SttError,
    LlmError,
    TtsError,
    RateLimit,
    InternalError,
    InvalidMessage,
    UserIdUpdateError,
}

impl std::fmt::Display for ErrorCode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let s = match self {
            ErrorCode::ConnectionFailed => "connection_failed",
            ErrorCode::ConnectionTimeout => "connection_timeout",
            ErrorCode::AuthenticationFailed => "authentication_failed",
            ErrorCode::SessionError => "session_error",
            ErrorCode::AudioError => "audio_error",
            ErrorCode::StreamingError => "streaming_error",
            ErrorCode::SttError => "stt_error",
            ErrorCode::LlmError => "llm_error",
            ErrorCode::TtsError => "tts_error",
            ErrorCode::RateLimit => "rate_limit",
            ErrorCode::InternalError => "internal_error",
            ErrorCode::InvalidMessage => "invalid_message",
            ErrorCode::UserIdUpdateError => "user_id_update_error",
        };
        write!(f, "{}", s)
    }
}

/// Connected event
#[derive(Debug, Clone)]
pub struct ConnectedEvent {
    pub connection_id: String,
    pub timestamp: String,
}

/// Disconnected event
#[derive(Debug, Clone)]
pub struct DisconnectedEvent {
    pub reason: DisconnectReason,
    pub code: Option<u16>,
    pub timestamp: String,
}

/// Session started event
#[derive(Debug, Clone)]
pub struct SessionStartedEvent {
    pub session_id: String,
    pub timestamp: String,
}

/// Session ended event
#[derive(Debug, Clone)]
pub struct SessionEndedEvent {
    pub session_id: String,
    pub timestamp: String,
}

/// Ready event - session is ready for audio input
#[derive(Debug, Clone)]
pub struct ReadyEvent {
    pub timestamp: String,
}

/// User transcript event - user's speech transcription
#[derive(Debug, Clone)]
pub struct UserTranscriptEvent {
    pub text: String,
    pub timestamp: String,
}

/// Response event - AI's text response
#[derive(Debug, Clone)]
pub struct ResponseEvent {
    pub text: String,
    pub is_final: bool,
    pub timestamp: String,
}

/// Audio event - AI's audio response
#[derive(Debug, Clone)]
pub struct AudioEvent {
    pub data: Vec<u8>,
    pub format: String,
    pub sample_rate: u32,
    pub timestamp: String,
}

/// Turn complete event - AI has finished its response turn
#[derive(Debug, Clone)]
pub struct TurnCompleteEvent {
    pub timestamp: String,
}

/// Session warning event - nearing duration limit
#[derive(Debug, Clone)]
pub struct SessionWarningEvent {
    pub remaining_seconds: u32,
    pub timestamp: String,
}

/// Session goodbye event - about to end
#[derive(Debug, Clone)]
pub struct SessionGoodbyeEvent {
    pub remaining_seconds: u32,
    pub timestamp: String,
}

/// Tool call event - AI wants to call a function
/// 
/// When the AI decides to execute a function you defined in `tools`,
/// you'll receive this event. You must call `send_tool_response()`
/// with the function result.
#[derive(Debug, Clone)]
pub struct ToolCallEvent {
    /// Unique ID for this tool call - use with send_tool_response
    pub id: String,
    /// Function name to execute
    pub name: String,
    /// Function arguments from AI
    pub args: serde_json::Value,
    /// Event timestamp
    pub timestamp: String,
}

/// User ID updated event - guest messages migrated to user partition
/// 
/// When a guest user logs in during a session, call `update_user_id()`
/// to migrate conversation history. This event confirms the migration.
#[derive(Debug, Clone)]
pub struct UserIdUpdatedEvent {
    /// The new user ID that was set
    pub user_id: String,
    /// Number of messages migrated from guest to user partition
    pub migrated_messages: usize,
    /// Event timestamp
    pub timestamp: String,
}

/// Interrupted event - AI response was interrupted by user speech (barge-in)
///
/// This event is emitted when the user starts speaking while the AI is responding.
/// **Critical**: When you receive this event, immediately clear your audio playback
/// buffer to stop the AI audio from continuing to play.
#[derive(Debug, Clone)]
pub struct InterruptedEvent {
    /// Event timestamp
    pub timestamp: String,
}

/// Error event
#[derive(Debug, Clone)]
pub struct ErrorEvent {
    pub code: ErrorCode,
    pub message: String,
    pub details: Option<String>,
    pub timestamp: String,
}

/// Reconnecting event
#[derive(Debug, Clone)]
pub struct ReconnectingEvent {
    pub attempt: u32,
    pub max_attempts: u32,
    pub delay_ms: u64,
    pub timestamp: String,
}

/// LiveSpeech event enum - all events emitted by the SDK
#[derive(Debug, Clone)]
pub enum LiveSpeechEvent {
    Connected(ConnectedEvent),
    Disconnected(DisconnectedEvent),
    Reconnecting(ReconnectingEvent),
    SessionStarted(SessionStartedEvent),
    SessionEnded(SessionEndedEvent),
    Ready(ReadyEvent),
    UserTranscript(UserTranscriptEvent),
    Response(ResponseEvent),
    Audio(AudioEvent),
    TurnComplete(TurnCompleteEvent),
    SessionWarning(SessionWarningEvent),
    SessionGoodbye(SessionGoodbyeEvent),
    ToolCall(ToolCallEvent),
    UserIdUpdated(UserIdUpdatedEvent),
    /// AI response was interrupted by user speech (barge-in)
    Interrupted(InterruptedEvent),
    Error(ErrorEvent),
}

/// Type alias for response handler
pub type ResponseHandler = Box<dyn Fn(&str, bool) + Send + Sync>;

/// Type alias for audio handler
pub type AudioHandler = Box<dyn Fn(&[u8]) + Send + Sync>;

/// Type alias for error handler
pub type ErrorHandler = Box<dyn Fn(&ErrorEvent) + Send + Sync>;
