//! Device identification commands
//!
//! Handles persistent device ID generation and retrieval for Jellyfin server communication.
//! TRACES: UR-009 | DR-011

use std::sync::Arc;

use log::info;
use tauri::State;
use uuid::Uuid;

use crate::commands::storage::DatabaseWrapper;
use crate::storage::db_service::{DatabaseService, Query, QueryParam};

/// Get or create the device ID.
/// Device ID is a UUID v4 that persists across app restarts.
/// On first call, generates and stores a new UUID.
/// On subsequent calls, retrieves the stored UUID.
///
/// # Returns
/// - `Ok(String)` - The device ID (UUID v4)
/// - `Err(String)` - If database operation fails
///
/// TRACES: UR-009 | DR-011
#[tauri::command]
#[specta::specta]
pub async fn device_get_id(db: State<'_, DatabaseWrapper>) -> Result<String, String> {
    let db_service = {
        let database = db.0.lock().map_err(|e| e.to_string())?;
        Arc::new(database.service())
    };

    // Try to get existing device ID from database
    let query = Query::with_params(
        "SELECT value FROM app_settings WHERE key = ?",
        vec![QueryParam::String("device_id".to_string())],
    );

    let existing_id: Option<String> = db_service
        .query_one(query, |row| row.get(0))
        .await
        .ok()
        .flatten();

    if let Some(device_id) = existing_id {
        info!("[Device] Retrieved existing device ID");
        return Ok(device_id);
    }

    // Generate new device ID
    let device_id = Uuid::new_v4().to_string();

    // Store it in database
    let insert_query = Query::with_params(
        "INSERT INTO app_settings (key, value) VALUES (?, ?)",
        vec![
            QueryParam::String("device_id".to_string()),
            QueryParam::String(device_id.clone()),
        ],
    );

    db_service
        .execute(insert_query)
        .await
        .map_err(|e| e.to_string())?;

    info!("[Device] Generated and stored new device ID");
    Ok(device_id)
}

/// Set the device ID (primarily for testing or recovery).
/// Overwrites any existing device ID.
///
/// # Arguments
/// * `device_id` - The device ID to store (should be UUID v4 format)
///
/// # Returns
/// - `Ok(())` - If device ID was stored successfully
/// - `Err(String)` - If database operation fails
///
/// TRACES: UR-009 | DR-011
#[tauri::command]
#[specta::specta]
pub async fn device_set_id(
    device_id: String,
    db: State<'_, DatabaseWrapper>,
) -> Result<(), String> {
    let db_service = {
        let database = db.0.lock().map_err(|e| e.to_string())?;
        Arc::new(database.service())
    };

    let query = Query::with_params(
        "INSERT OR REPLACE INTO app_settings (key, value) VALUES (?, ?)",
        vec![
            QueryParam::String("device_id".to_string()),
            QueryParam::String(device_id),
        ],
    );

    db_service.execute(query).await.map_err(|e| e.to_string())?;

    info!("[Device] Device ID set");
    Ok(())
}

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

    #[test]
    fn test_device_id_is_valid_uuid() {
        let id = Uuid::new_v4().to_string();
        // Should parse as UUID
        let parsed = Uuid::parse_str(&id);
        assert!(parsed.is_ok(), "Device ID should be a valid UUID");
    }

    #[test]
    fn test_device_id_format() {
        let id = Uuid::new_v4().to_string();
        // UUID v4 format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx (36 chars with hyphens)
        assert_eq!(id.len(), 36, "Device ID should be 36 characters");
        assert!(id.contains('-'), "Device ID should contain hyphens");
    }

    #[test]
    fn test_device_ids_are_unique() {
        let id1 = Uuid::new_v4().to_string();
        let id2 = Uuid::new_v4().to_string();
        assert_ne!(id1, id2, "Generated device IDs should be unique");
    }
}
