use serde::Serialize;

/// Configuration for Jellyfin API client
#[derive(Debug, Clone)]
pub struct JellyfinConfig {
    pub server_url: String,
    pub access_token: String,
    pub device_id: String,
}

/// Request body for reporting playback start
#[derive(Debug, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct PlaybackStartRequest {
    pub item_id: String,
    pub position_ticks: i64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub play_session_id: Option<String>,
    pub play_command: String,
    pub is_paused: bool,
}

/// Request body for reporting playback stopped
#[derive(Debug, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct PlaybackStoppedRequest {
    pub item_id: String,
    pub position_ticks: i64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub play_session_id: Option<String>,
}

/// Request body for reporting playback progress
#[derive(Debug, Serialize)]
#[serde(rename_all = "PascalCase")]
#[allow(dead_code)] // Will be used when playback_reporting is integrated
pub struct PlaybackProgressRequest {
    pub item_id: String,
    pub position_ticks: i64,
    pub is_paused: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub play_session_id: Option<String>,
}

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

    #[test]
    fn test_jellyfin_config_creation() {
        let config = JellyfinConfig {
            server_url: "https://jellyfin.example.com".to_string(),
            access_token: "token-123".to_string(),
            device_id: "device-456".to_string(),
        };

        assert_eq!(config.server_url, "https://jellyfin.example.com");
        assert_eq!(config.access_token, "token-123");
        assert_eq!(config.device_id, "device-456");
    }

    #[test]
    fn test_jellyfin_config_clone() {
        let config = JellyfinConfig {
            server_url: "https://server.local".to_string(),
            access_token: "token-abc".to_string(),
            device_id: "device-xyz".to_string(),
        };

        let cloned = config.clone();
        assert_eq!(config.server_url, cloned.server_url);
        assert_eq!(config.access_token, cloned.access_token);
        assert_eq!(config.device_id, cloned.device_id);
    }

    #[test]
    fn test_playback_start_request_serialization() {
        let request = PlaybackStartRequest {
            item_id: "item-123".to_string(),
            position_ticks: 0,
            play_session_id: Some("session-456".to_string()),
            play_command: "PlayNow".to_string(),
            is_paused: false,
        };

        let json = serde_json::to_string(&request);
        assert!(json.is_ok());
        let serialized = json.unwrap();
        assert!(serialized.contains("item-123"));
        assert!(serialized.contains("PlayNow"));
    }

    #[test]
    fn test_playback_start_request_without_session() {
        let request = PlaybackStartRequest {
            item_id: "item-789".to_string(),
            position_ticks: 1_000_000,
            play_session_id: None,
            play_command: "Resume".to_string(),
            is_paused: true,
        };

        let json = serde_json::to_string(&request).unwrap();
        assert!(json.contains("true"));
        assert!(!json.contains("playSessionId"));
    }

    #[test]
    fn test_playback_start_request_pascal_case() {
        let request = PlaybackStartRequest {
            item_id: "item-1".to_string(),
            position_ticks: 100,
            play_session_id: Some("session-1".to_string()),
            play_command: "Play".to_string(),
            is_paused: false,
        };

        let json = serde_json::to_string(&request).unwrap();
        // Verify PascalCase serialization
        assert!(json.contains("ItemId"));
        assert!(json.contains("PositionTicks"));
        assert!(json.contains("PlaySessionId"));
        assert!(json.contains("PlayCommand"));
        assert!(json.contains("IsPaused"));
    }

    #[test]
    fn test_playback_stopped_request_serialization() {
        let request = PlaybackStoppedRequest {
            item_id: "item-555".to_string(),
            position_ticks: 5_000_000,
            play_session_id: Some("session-789".to_string()),
        };

        let json = serde_json::to_string(&request);
        assert!(json.is_ok());
        let serialized = json.unwrap();
        assert!(serialized.contains("item-555"));
        assert!(serialized.contains("5000000"));
    }

    #[test]
    fn test_playback_stopped_request_without_session() {
        let request = PlaybackStoppedRequest {
            item_id: "item-000".to_string(),
            position_ticks: 0,
            play_session_id: None,
        };

        let json = serde_json::to_string(&request).unwrap();
        assert!(json.contains("item-000"));
        assert!(!json.contains("playSessionId"));
    }

    #[test]
    fn test_playback_stopped_request_pascal_case() {
        let request = PlaybackStoppedRequest {
            item_id: "i1".to_string(),
            position_ticks: 500,
            play_session_id: Some("s1".to_string()),
        };

        let json = serde_json::to_string(&request).unwrap();
        assert!(json.contains("ItemId"));
        assert!(json.contains("PositionTicks"));
        assert!(json.contains("PlaySessionId"));
    }

    #[test]
    fn test_playback_progress_request_serialization() {
        let request = PlaybackProgressRequest {
            item_id: "item-999".to_string(),
            position_ticks: 150_000_000,
            is_paused: false,
            play_session_id: Some("session-111".to_string()),
        };

        let json = serde_json::to_string(&request);
        assert!(json.is_ok());
        let serialized = json.unwrap();
        assert!(serialized.contains("item-999"));
        assert!(serialized.contains("150000000"));
        assert!(serialized.contains("false"));
    }

    #[test]
    fn test_playback_progress_request_paused() {
        let request = PlaybackProgressRequest {
            item_id: "item-paused".to_string(),
            position_ticks: 75_000_000,
            is_paused: true,
            play_session_id: None,
        };

        let json = serde_json::to_string(&request).unwrap();
        assert!(json.contains("true"));
        assert!(json.contains("item-paused"));
    }

    #[test]
    fn test_playback_progress_request_pascal_case() {
        let request = PlaybackProgressRequest {
            item_id: "i2".to_string(),
            position_ticks: 200,
            is_paused: true,
            play_session_id: Some("s2".to_string()),
        };

        let json = serde_json::to_string(&request).unwrap();
        assert!(json.contains("ItemId"));
        assert!(json.contains("PositionTicks"));
        assert!(json.contains("IsPaused"));
        assert!(json.contains("PlaySessionId"));
    }

    #[test]
    fn test_playback_start_request_various_position_values() {
        let positions = vec![0, 1_000_000, 10_000_000, 100_000_000, 1_000_000_000];

        for pos in positions {
            let request = PlaybackStartRequest {
                item_id: "item-test".to_string(),
                position_ticks: pos,
                play_session_id: None,
                play_command: "Play".to_string(),
                is_paused: false,
            };

            let json = serde_json::to_string(&request).unwrap();
            assert!(json.contains("item-test"));
            assert!(json.contains(&pos.to_string()));
        }
    }

    #[test]
    fn test_jellyfin_config_debug() {
        let config = JellyfinConfig {
            server_url: "https://debug.example.com".to_string(),
            access_token: "token-debug".to_string(),
            device_id: "device-debug".to_string(),
        };

        let debug_str = format!("{:?}", config);
        assert!(debug_str.contains("JellyfinConfig"));
        assert!(debug_str.contains("https://debug.example.com"));
    }

    #[test]
    fn test_playback_requests_creation_patterns() {
        // Test various creation patterns
        let start = PlaybackStartRequest {
            item_id: "i1".to_string(),
            position_ticks: 0,
            play_session_id: None,
            play_command: "PlayNow".to_string(),
            is_paused: false,
        };

        let stopped = PlaybackStoppedRequest {
            item_id: "i1".to_string(),
            position_ticks: 1_000_000,
            play_session_id: None,
        };

        let progress = PlaybackProgressRequest {
            item_id: "i1".to_string(),
            position_ticks: 500_000,
            is_paused: false,
            play_session_id: None,
        };

        // All should serialize successfully
        assert!(serde_json::to_string(&start).is_ok());
        assert!(serde_json::to_string(&stopped).is_ok());
        assert!(serde_json::to_string(&progress).is_ok());
    }
}
