//! IPC protocol definitions.
//!
//! Defines the message format and commands for IPC communication.

use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::config::Settings;
use crate::recording::RecordingResult;
use crate::state::DaemonStatus;
use crate::timeline::RecordingMetadata;

/// IPC message wrapper.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IpcMessage {
    /// Message type.
    #[serde(rename = "type")]
    pub message_type: MessageType,
    /// Unique message ID.
    pub id: Uuid,
    /// Command name (for requests).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub command: Option<IpcCommand>,
    /// Payload data.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payload: Option<serde_json::Value>,
}

/// Message type.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MessageType {
    /// Request message (expects response).
    Request,
    /// Response message.
    Response,
    /// Notification message (no response expected).
    Notification,
}

/// IPC commands that can be sent to the daemon.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum IpcCommand {
    // Configuration commands
    /// Get current settings.
    GetSettings,
    /// Update settings.
    UpdateSettings,
    /// Reset settings to defaults.
    ResetSettings,

    // Status commands
    /// Get daemon status.
    GetStatus,
    /// Get available encoders.
    GetEncoders,

    // Recording commands
    /// Start recording manually.
    StartRecording,
    /// Stop recording.
    StopRecording,
    /// Get list of recordings.
    GetRecordings,
    /// Get specific recording details.
    GetRecording { id: Uuid },
    /// Delete a recording.
    DeleteRecording { id: Uuid },

    // Timeline commands
    /// Get timeline for a recording.
    GetTimeline { recording_id: Uuid },
    /// Export timeline to file.
    ExportTimeline {
        recording_id: Uuid,
        format: ExportFormat,
    },

    // Daemon control
    /// Shutdown the daemon.
    Shutdown,
}

/// Export format for timeline.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ExportFormat {
    /// JSON format.
    Json,
    /// CSV format.
    Csv,
}

/// IPC response.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IpcResponse {
    /// Original request ID.
    pub request_id: Uuid,
    /// Whether the request was successful.
    pub success: bool,
    /// Response data (if successful).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub data: Option<serde_json::Value>,
    /// Error message (if failed).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<String>,
}

impl IpcResponse {
    /// Create a successful response.
    pub fn success(request_id: Uuid, data: impl Serialize) -> Self {
        Self {
            request_id,
            success: true,
            data: Some(serde_json::to_value(data).unwrap_or(serde_json::Value::Null)),
            error: None,
        }
    }

    /// Create an error response.
    pub fn error(request_id: Uuid, error: impl Into<String>) -> Self {
        Self {
            request_id,
            success: false,
            data: None,
            error: Some(error.into()),
        }
    }

    /// Serialize response to JSON string.
    pub fn to_json(&self) -> Result<String, serde_json::Error> {
        serde_json::to_string(self)
    }
}

/// IPC notification (server-initiated message).
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "notification", rename_all = "camelCase")]
pub enum IpcNotification {
    /// Recording started.
    RecordingStarted {
        recording_id: Uuid,
        game_id: Option<u64>,
        champion: Option<String>,
    },

    /// Recording stopped.
    RecordingStopped {
        recording_id: Uuid,
        result: RecordingResult,
    },

    /// Game event received.
    GameEvent {
        event_type: String,
        raw_data: serde_json::Value,
    },

    /// Daemon status changed.
    StatusChanged { status: DaemonStatus },

    /// League Client connection changed.
    ClientConnectionChanged { connected: bool },

    /// Settings updated.
    SettingsUpdated { settings: Settings },

    /// Error occurred.
    Error { message: String },
}

/// Response data types for specific commands.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetSettingsResponse {
    pub settings: Settings,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetStatusResponse {
    pub status: DaemonStatus,
    pub is_recording: bool,
    pub current_game_id: Option<u64>,
    pub client_connected: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetRecordingsResponse {
    pub recordings: Vec<RecordingMetadata>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetEncodersResponse {
    pub available: Vec<EncoderInfo>,
    pub current: String,
}

/// Information about an available encoder.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EncoderInfo {
    /// Encoder ID.
    pub id: String,
    /// Display name.
    pub name: String,
    /// Whether this is a hardware encoder.
    pub is_hardware: bool,
    /// Whether this encoder is available on this system.
    pub available: bool,
}

impl IpcMessage {
    /// Create a new request message.
    pub fn request(command: IpcCommand, payload: Option<serde_json::Value>) -> Self {
        Self {
            message_type: MessageType::Request,
            id: Uuid::new_v4(),
            command: Some(command),
            payload,
        }
    }

    /// Create a new notification message.
    pub fn notification(notification: IpcNotification) -> Self {
        Self {
            message_type: MessageType::Notification,
            id: Uuid::new_v4(),
            command: None,
            payload: Some(serde_json::to_value(notification).unwrap_or(serde_json::Value::Null)),
        }
    }

    /// Parse from JSON string.
    pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
        serde_json::from_str(json)
    }

    /// Serialize to JSON string.
    pub fn to_json(&self) -> Result<String, serde_json::Error> {
        serde_json::to_string(self)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_ipc_message_creation() {
        let msg = IpcMessage::request(IpcCommand::GetSettings, None);
        assert_eq!(msg.message_type, MessageType::Request);
        assert_eq!(msg.command, Some(IpcCommand::GetSettings));
    }

    #[test]
    fn test_ipc_response_success() {
        let id = Uuid::new_v4();
        let response = IpcResponse::success(id, "test data");
        assert!(response.success);
        assert!(response.data.is_some());
        assert!(response.error.is_none());
    }

    #[test]
    fn test_ipc_response_error() {
        let id = Uuid::new_v4();
        let response = IpcResponse::error(id, "Something went wrong");
        assert!(!response.success);
        assert!(response.data.is_none());
        assert!(response.error.is_some());
    }

    #[test]
    fn test_message_serialization() {
        let msg = IpcMessage::request(IpcCommand::GetStatus, None);
        let json = msg.to_json().unwrap();
        let parsed = IpcMessage::from_json(&json).unwrap();
        assert_eq!(msg.command, parsed.command);
    }
}
