//! Smart-cache statistics/config and album recommendation commands.
//!
//! TRACES: UR-045 | DR-057

use log::info;
use std::sync::Arc;
use tauri::State;

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

/// SmartCache statistics
#[derive(specta::Type, Debug, Clone, serde::Serialize)]
pub struct SmartCacheStats {
    pub total_size: u64,
    pub storage_limit: u64,
    pub available_space: u64,
    pub items_count: i64,
    pub config: crate::download::cache::CacheConfig,
}

/// Get SmartCache statistics
#[tauri::command]
#[specta::specta]
pub async fn get_smart_cache_stats(
    db: State<'_, DatabaseWrapper>,
    smart_cache: State<'_, SmartCacheWrapper>,
    user_id: String,
) -> Result<SmartCacheStats, String> {
    let db_service = {
        let database = db.0.lock().map_err(|e| e.to_string())?;
        Arc::new(database.service())
    };

    // Clone cache to avoid holding lock across async operations
    let cache = {
        let guard = smart_cache.0.lock().map_err(|e| e.to_string())?;
        guard.clone()
    };

    let total_size = cache
        .get_total_download_size_async(&db_service, &user_id)
        .await?;

    let config = cache
        .get_config()
        .ok_or_else(|| "Failed to get cache config".to_string())?;

    let storage_limit = config.storage_limit;
    let available_space = storage_limit.saturating_sub(total_size);

    // Get item count
    let count_query = Query::with_params(
        "SELECT COUNT(*) FROM downloads WHERE user_id = ? AND status = 'completed'",
        vec![QueryParam::String(user_id)],
    );
    let items_count: i64 = db_service
        .query_one(count_query, |row| row.get(0))
        .await
        .unwrap_or(0);

    Ok(SmartCacheStats {
        total_size,
        storage_limit,
        available_space,
        items_count,
        config,
    })
}

/// Update SmartCache configuration
#[tauri::command]
#[specta::specta]
pub async fn update_smart_cache_config(
    smart_cache: State<'_, SmartCacheWrapper>,
    config: crate::download::cache::CacheConfig,
) -> Result<(), String> {
    let cache = smart_cache.0.lock().map_err(|e| e.to_string())?;
    cache.update_config(config);
    info!("Updated SmartCache configuration");
    Ok(())
}

/// Get SmartCache configuration
#[tauri::command]
#[specta::specta]
pub async fn get_smart_cache_config(
    smart_cache: State<'_, SmartCacheWrapper>,
) -> Result<crate::download::cache::CacheConfig, String> {
    let cache = smart_cache.0.lock().map_err(|e| e.to_string())?;
    cache
        .get_config()
        .ok_or_else(|| "Failed to get cache config".to_string())
}

/// Album recommendation info
#[derive(specta::Type, Debug, Clone, serde::Serialize)]
pub struct AlbumRecommendation {
    pub album_id: String,
    pub album_name: String,
    pub tracks_played: usize,
    pub total_tracks: usize,
    pub should_download: bool,
}

/// Album affinity status info
#[derive(specta::Type, Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AlbumAffinityStatus {
    pub album_id: String,
    pub unique_tracks_played: usize,
    pub threshold: usize,
    pub threshold_reached: bool,
}

/// Get album recommendations based on play history
#[tauri::command]
#[specta::specta]
pub async fn get_album_recommendations(
    db: State<'_, DatabaseWrapper>,
    smart_cache: State<'_, SmartCacheWrapper>,
    user_id: String,
) -> Result<Vec<AlbumRecommendation>, String> {
    let db_service = {
        let database = db.0.lock().map_err(|e| e.to_string())?;
        Arc::new(database.service())
    };

    // Clone cache to avoid holding lock across async operations
    let cache = {
        let guard = smart_cache.0.lock().map_err(|e| e.to_string())?;
        guard.clone()
    };

    // Get all albums that user has played tracks from
    let query = Query::with_params(
        "SELECT DISTINCT i.album_id, a.name
         FROM user_data ud
         JOIN items i ON ud.item_id = i.id
         JOIN items a ON i.album_id = a.id
         WHERE ud.user_id = ?
           AND ud.play_count > 0
           AND i.item_type = 'Audio'
           AND i.album_id IS NOT NULL",
        vec![QueryParam::String(user_id.clone())],
    );

    let albums: Vec<(String, String)> = db_service
        .query_many(query, |row| Ok((row.get(0)?, row.get(1)?)))
        .await
        .unwrap_or_default();

    let mut recommendations = Vec::new();

    for (album_id, album_name) in albums {
        // Check if should cache
        let should_download = cache.should_cache_album(&album_id).unwrap_or(false);

        // Get track counts
        let tracks_query = Query::with_params(
            "SELECT
                COUNT(*) as total,
                COUNT(ud.id) as played
             FROM items i
             LEFT JOIN user_data ud ON i.id = ud.item_id AND ud.user_id = ?
             WHERE i.album_id = ? AND i.item_type = 'Audio'",
            vec![
                QueryParam::String(user_id.clone()),
                QueryParam::String(album_id.clone()),
            ],
        );

        let (total_tracks, tracks_played): (i64, i64) = db_service
            .query_one(tracks_query, |row| Ok((row.get(0)?, row.get(1)?)))
            .await
            .unwrap_or((0, 0));

        if tracks_played > 0 {
            recommendations.push(AlbumRecommendation {
                album_id,
                album_name,
                tracks_played: tracks_played as usize,
                total_tracks: total_tracks as usize,
                should_download,
            });
        }
    }

    // Sort by tracks played (descending)
    recommendations.sort_by(|a, b| b.tracks_played.cmp(&a.tracks_played));

    Ok(recommendations)
}

/// Get album affinity status for all tracked albums
/// This shows the SmartCache's internal play history and threshold status
#[tauri::command]
#[specta::specta]
pub fn get_album_affinity_status(
    smart_cache: State<'_, SmartCacheWrapper>,
) -> Result<Vec<AlbumAffinityStatus>, String> {
    let cache = smart_cache.0.lock().map_err(|e| e.to_string())?;

    // Get the threshold from config
    let threshold = cache
        .get_config()
        .map(|c| c.album_affinity_threshold)
        .unwrap_or(3);

    // Get all tracked albums with their play counts
    let play_history = cache.get_album_play_history();

    let mut statuses: Vec<AlbumAffinityStatus> = play_history
        .into_iter()
        .map(|(album_id, unique_tracks_played)| {
            let threshold_reached = unique_tracks_played >= threshold;
            AlbumAffinityStatus {
                album_id,
                unique_tracks_played,
                threshold,
                threshold_reached,
            }
        })
        .collect();

    // Sort by play count (descending)
    statuses.sort_by(|a, b| b.unique_tracks_played.cmp(&a.unique_tracks_played));

    Ok(statuses)
}
