//! Tauri commands for playback reporting operations
//!
//! TRACES: UR-025, UR-019 | IR-015, JA-010, JA-011, JA-012 | DR-028
//!
//! These commands provide frontend access to the Rust playback reporting system,
//! replacing the TypeScript implementation with native Rust reporting.
//!
//! Commands are registered but not yet called from the frontend.
//! Dead code warnings are suppressed until frontend migration is complete.

#![allow(dead_code)]

use std::sync::Arc;
use tauri::State;
use tokio::sync::Mutex as TokioMutex;

use crate::commands::connectivity::ConnectivityMonitorWrapper;
use crate::commands::storage::DatabaseWrapper;
use crate::jellyfin::client::JellyfinClient;
use crate::jellyfin::JellyfinConfig;
use crate::playback_reporting::{PlaybackContext, PlaybackOperation, PlaybackReporter};
use crate::utils::conversions::seconds_to_ticks;

/// Tauri state wrapper for PlaybackReporter
pub struct PlaybackReporterWrapper(pub Arc<TokioMutex<Option<PlaybackReporter>>>);

/// Initialize playback reporter (called after login)
#[tauri::command]
#[specta::specta]
pub async fn playback_reporter_init(
    reporter_wrapper: State<'_, PlaybackReporterWrapper>,
    db: State<'_, DatabaseWrapper>,
    server_url: String,
    user_id: String,
    access_token: String,
    device_id: String,
) -> Result<(), String> {
    log::info!("[PlaybackReporter] Initializing for user: {}", user_id);

    // Get database service
    let db_service = {
        let database = db.0.lock().map_err(|e| e.to_string())?;
        Arc::new(database.service())
    };

    // Create JellyfinClient
    let jellyfin_config = JellyfinConfig {
        server_url,
        access_token,
        device_id,
    };

    let jellyfin_client = JellyfinClient::new(jellyfin_config)
        .map_err(|e| format!("Failed to create JellyfinClient: {}", e))?;

    // Create PlaybackReporter
    let reporter = PlaybackReporter::new(
        db_service,
        Arc::new(TokioMutex::new(Some(jellyfin_client))),
        user_id.clone(),
    );

    // Store in wrapper
    *reporter_wrapper.0.lock().await = Some(reporter);

    log::info!(
        "[PlaybackReporter] Initialized successfully for user: {}",
        user_id
    );
    Ok(())
}

/// Destroy playback reporter (called on logout)
#[tauri::command]
#[specta::specta]
pub async fn playback_reporter_destroy(
    reporter_wrapper: State<'_, PlaybackReporterWrapper>,
) -> Result<(), String> {
    log::info!("[PlaybackReporter] Destroying reporter");
    *reporter_wrapper.0.lock().await = None;
    Ok(())
}

/// Report playback start
#[tauri::command]
#[specta::specta]
pub async fn playback_report_start(
    reporter: State<'_, PlaybackReporterWrapper>,
    connectivity: State<'_, ConnectivityMonitorWrapper>,
    item_id: String,
    position_seconds: f64,
    context_type: Option<String>,
    context_id: Option<String>,
) -> Result<(), String> {
    let reporter_guard = reporter.0.lock().await;
    let reporter_instance = reporter_guard
        .as_ref()
        .ok_or("PlaybackReporter not initialized")?;

    let position_ticks = seconds_to_ticks(position_seconds);
    let context = context_type.map(|ct| PlaybackContext {
        context_type: ct,
        context_id,
    });

    let operation = PlaybackOperation::Start {
        item_id,
        position_ticks,
        context,
    };

    let monitor = connectivity.0.lock().await;
    let is_online = monitor.get_status().await.is_server_reachable;
    drop(monitor);

    reporter_instance.report(operation, is_online).await
}

/// Report playback progress
#[tauri::command]
#[specta::specta]
pub async fn playback_report_progress(
    reporter: State<'_, PlaybackReporterWrapper>,
    connectivity: State<'_, ConnectivityMonitorWrapper>,
    item_id: String,
    position_seconds: f64,
    is_paused: bool,
) -> Result<(), String> {
    let reporter_guard = reporter.0.lock().await;
    let reporter_instance = reporter_guard
        .as_ref()
        .ok_or("PlaybackReporter not initialized")?;

    let position_ticks = seconds_to_ticks(position_seconds);
    let operation = PlaybackOperation::Progress {
        item_id,
        position_ticks,
        is_paused,
    };

    let monitor = connectivity.0.lock().await;
    let is_online = monitor.get_status().await.is_server_reachable;
    drop(monitor);

    reporter_instance.report(operation, is_online).await
}

/// Report playback stopped
#[tauri::command]
#[specta::specta]
pub async fn playback_report_stopped(
    reporter: State<'_, PlaybackReporterWrapper>,
    connectivity: State<'_, ConnectivityMonitorWrapper>,
    item_id: String,
    position_seconds: f64,
) -> Result<(), String> {
    let reporter_guard = reporter.0.lock().await;
    let reporter_instance = reporter_guard
        .as_ref()
        .ok_or("PlaybackReporter not initialized")?;

    let position_ticks = seconds_to_ticks(position_seconds);
    let operation = PlaybackOperation::Stopped {
        item_id,
        position_ticks,
    };

    let monitor = connectivity.0.lock().await;
    let is_online = monitor.get_status().await.is_server_reachable;
    drop(monitor);

    reporter_instance.report(operation, is_online).await
}

/// Mark item as played
#[tauri::command]
#[specta::specta]
pub async fn playback_mark_played(
    reporter: State<'_, PlaybackReporterWrapper>,
    connectivity: State<'_, ConnectivityMonitorWrapper>,
    item_id: String,
) -> Result<(), String> {
    let reporter_guard = reporter.0.lock().await;
    let reporter_instance = reporter_guard
        .as_ref()
        .ok_or("PlaybackReporter not initialized")?;

    let operation = PlaybackOperation::MarkPlayed { item_id };

    let monitor = connectivity.0.lock().await;
    let is_online = monitor.get_status().await.is_server_reachable;
    drop(monitor);

    reporter_instance.report(operation, is_online).await
}

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

    #[test]
    fn test_playback_operation_start_creation() {
        let operation = PlaybackOperation::Start {
            item_id: "item-123".to_string(),
            position_ticks: 15_000_000,
            context: Some(PlaybackContext {
                context_type: "series".to_string(),
                context_id: Some("series-456".to_string()),
            }),
        };

        // Verify enum variant can be created and pattern matched
        if let PlaybackOperation::Start {
            item_id,
            position_ticks,
            context,
        } = operation
        {
            assert_eq!(item_id, "item-123");
            assert_eq!(position_ticks, 15_000_000);
            assert!(context.is_some());
            let ctx = context.unwrap();
            assert_eq!(ctx.context_type, "series");
            assert_eq!(ctx.context_id, Some("series-456".to_string()));
        } else {
            panic!("Expected Start variant");
        }
    }

    #[test]
    fn test_playback_operation_start_without_context() {
        let operation = PlaybackOperation::Start {
            item_id: "item-789".to_string(),
            position_ticks: 5_000_000,
            context: None,
        };

        if let PlaybackOperation::Start {
            item_id, context, ..
        } = operation
        {
            assert_eq!(item_id, "item-789");
            assert!(context.is_none());
        } else {
            panic!("Expected Start variant");
        }
    }

    #[test]
    fn test_playback_operation_progress_creation() {
        let operation = PlaybackOperation::Progress {
            item_id: "item-999".to_string(),
            position_ticks: 30_000_000,
            is_paused: true,
        };

        if let PlaybackOperation::Progress {
            item_id,
            position_ticks,
            is_paused,
        } = operation
        {
            assert_eq!(item_id, "item-999");
            assert_eq!(position_ticks, 30_000_000);
            assert!(is_paused);
        } else {
            panic!("Expected Progress variant");
        }
    }

    #[test]
    fn test_playback_operation_progress_playing() {
        let operation = PlaybackOperation::Progress {
            item_id: "item-555".to_string(),
            position_ticks: 45_000_000,
            is_paused: false,
        };

        if let PlaybackOperation::Progress { is_paused, .. } = operation {
            assert!(!is_paused);
        } else {
            panic!("Expected Progress variant");
        }
    }

    #[test]
    fn test_playback_operation_stopped_creation() {
        let operation = PlaybackOperation::Stopped {
            item_id: "item-111".to_string(),
            position_ticks: 120_000_000,
        };

        if let PlaybackOperation::Stopped {
            item_id,
            position_ticks,
        } = operation
        {
            assert_eq!(item_id, "item-111");
            assert_eq!(position_ticks, 120_000_000);
        } else {
            panic!("Expected Stopped variant");
        }
    }

    #[test]
    fn test_playback_operation_mark_played_creation() {
        let operation = PlaybackOperation::MarkPlayed {
            item_id: "item-222".to_string(),
        };

        if let PlaybackOperation::MarkPlayed { item_id } = operation {
            assert_eq!(item_id, "item-222");
        } else {
            panic!("Expected MarkPlayed variant");
        }
    }

    #[test]
    fn test_playback_context_with_series() {
        let context = PlaybackContext {
            context_type: "series".to_string(),
            context_id: Some("series-789".to_string()),
        };

        assert_eq!(context.context_type, "series");
        assert_eq!(context.context_id, Some("series-789".to_string()));
    }

    #[test]
    fn test_playback_context_without_id() {
        let context = PlaybackContext {
            context_type: "folder".to_string(),
            context_id: None,
        };

        assert_eq!(context.context_type, "folder");
        assert!(context.context_id.is_none());
    }

    #[test]
    fn test_playback_context_clone() {
        let context = PlaybackContext {
            context_type: "container".to_string(),
            context_id: Some("container-123".to_string()),
        };

        let cloned = context.clone();
        assert_eq!(cloned.context_type, "container");
        assert_eq!(cloned.context_id, Some("container-123".to_string()));
    }

    #[test]
    fn test_seconds_to_ticks_conversion() {
        assert_eq!(seconds_to_ticks(0.0), 0);
        assert_eq!(seconds_to_ticks(1.0), 10_000_000);
        assert_eq!(seconds_to_ticks(1.5), 15_000_000);
        assert_eq!(seconds_to_ticks(120.0), 1_200_000_000);
    }

    #[test]
    fn test_playback_reporter_wrapper_structure() {
        // Verify wrapper type can hold Arc<TokioMutex<Option<T>>>
        assert_eq!(std::mem::size_of::<PlaybackReporterWrapper>() > 0, true);
    }

    #[test]
    fn test_playback_operation_debug_trait() {
        // Verify Debug trait is implemented for operations
        let operation = PlaybackOperation::Start {
            item_id: "item-1".to_string(),
            position_ticks: 0,
            context: None,
        };

        let debug_str = format!("{:?}", operation);
        assert!(debug_str.contains("Start"));
        assert!(debug_str.contains("item-1"));
    }

    #[test]
    fn test_playback_operation_clone() {
        let operation = PlaybackOperation::Progress {
            item_id: "item-clone".to_string(),
            position_ticks: 50_000_000,
            is_paused: true,
        };

        let cloned = operation.clone();
        if let PlaybackOperation::Progress {
            item_id, is_paused, ..
        } = cloned
        {
            assert_eq!(item_id, "item-clone");
            assert!(is_paused);
        } else {
            panic!("Clone failed to preserve variant");
        }
    }

    #[test]
    fn test_playback_operation_all_variants() {
        // Test that all operation variants can be created and matched
        let start_op = PlaybackOperation::Start {
            item_id: "i1".to_string(),
            position_ticks: 0,
            context: None,
        };
        assert!(matches!(start_op, PlaybackOperation::Start { .. }));

        let progress_op = PlaybackOperation::Progress {
            item_id: "i2".to_string(),
            position_ticks: 100,
            is_paused: false,
        };
        assert!(matches!(progress_op, PlaybackOperation::Progress { .. }));

        let stopped_op = PlaybackOperation::Stopped {
            item_id: "i3".to_string(),
            position_ticks: 200,
        };
        assert!(matches!(stopped_op, PlaybackOperation::Stopped { .. }));

        let played_op = PlaybackOperation::MarkPlayed {
            item_id: "i4".to_string(),
        };
        assert!(matches!(played_op, PlaybackOperation::MarkPlayed { .. }));
    }
}
