// Clipboard history module for tracking and managing copied code snippets
// Implements issue #25 - Clipboard History feature

use serde::{Deserialize, Serialize};
use std::sync::Mutex;
use tauri_plugin_store::StoreExt;
use uuid::Uuid;

const STORE_FILE: &str = "hikari-clipboard.json";
const HISTORY_KEY: &str = "clipboard_history";
const MAX_HISTORY_SIZE: usize = 100;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClipboardEntry {
    pub id: String,
    pub content: String,
    pub language: Option<String>,
    pub source: Option<String>,
    pub timestamp: String,
    pub is_pinned: bool,
}

impl ClipboardEntry {
    pub fn new(content: String, language: Option<String>, source: Option<String>) -> Self {
        Self {
            id: Uuid::new_v4().to_string(),
            content,
            language,
            source,
            timestamp: chrono::Utc::now().to_rfc3339(),
            is_pinned: false,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
struct ClipboardHistory {
    entries: Vec<ClipboardEntry>,
}

// Track last clipboard content to avoid duplicates
#[derive(Default)]
struct ClipboardState {
    last_content: Option<String>,
}

static CLIPBOARD_STATE: Mutex<ClipboardState> = Mutex::new(ClipboardState { last_content: None });

fn load_history(app: &tauri::AppHandle) -> ClipboardHistory {
    let store = app.store(STORE_FILE).ok();
    store
        .and_then(|s| s.get(HISTORY_KEY))
        .and_then(|v| serde_json::from_value(v.clone()).ok())
        .unwrap_or_default()
}

fn save_history(app: &tauri::AppHandle, history: &ClipboardHistory) -> Result<(), String> {
    let store = app.store(STORE_FILE).map_err(|e| e.to_string())?;
    store.set(
        HISTORY_KEY,
        serde_json::to_value(history).map_err(|e| e.to_string())?,
    );
    store.save().map_err(|e| e.to_string())?;
    Ok(())
}

/// List all clipboard entries, optionally filtered by language
#[tauri::command]
pub fn list_clipboard_entries(
    app: tauri::AppHandle,
    language: Option<String>,
) -> Result<Vec<ClipboardEntry>, String> {
    let history = load_history(&app);
    let entries = if let Some(lang) = language {
        history
            .entries
            .into_iter()
            .filter(|e| e.language.as_ref() == Some(&lang))
            .collect()
    } else {
        history.entries
    };
    Ok(entries)
}

/// Capture current clipboard content and add to history
#[tauri::command]
pub fn capture_clipboard(
    app: tauri::AppHandle,
    content: String,
    language: Option<String>,
    source: Option<String>,
) -> Result<ClipboardEntry, String> {
    // Check for duplicate (same content as last capture)
    {
        let mut state = CLIPBOARD_STATE.lock().map_err(|e| e.to_string())?;
        if state.last_content.as_ref() == Some(&content) {
            // Return existing entry if content is the same
            let history = load_history(&app);
            if let Some(entry) = history.entries.first() {
                if entry.content == content {
                    return Ok(entry.clone());
                }
            }
        }
        state.last_content = Some(content.clone());
    }

    let entry = ClipboardEntry::new(content, language, source);
    let mut history = load_history(&app);

    // Add to front of history
    history.entries.insert(0, entry.clone());

    // Enforce max size (keep pinned entries)
    let mut pinned: Vec<ClipboardEntry> = history
        .entries
        .iter()
        .filter(|e| e.is_pinned)
        .cloned()
        .collect();
    let mut unpinned: Vec<ClipboardEntry> = history
        .entries
        .into_iter()
        .filter(|e| !e.is_pinned)
        .collect();

    // Trim unpinned entries if over max size
    if unpinned.len() + pinned.len() > MAX_HISTORY_SIZE {
        let max_unpinned = MAX_HISTORY_SIZE.saturating_sub(pinned.len());
        unpinned.truncate(max_unpinned);
    }

    // Merge back, pinned first then unpinned
    pinned.extend(unpinned);
    history.entries = pinned;

    // Sort by timestamp descending (newest first), pinned entries stay at top
    history.entries.sort_by(|a, b| {
        if a.is_pinned && !b.is_pinned {
            std::cmp::Ordering::Less
        } else if !a.is_pinned && b.is_pinned {
            std::cmp::Ordering::Greater
        } else {
            b.timestamp.cmp(&a.timestamp)
        }
    });

    save_history(&app, &history)?;
    Ok(entry)
}

/// Delete a clipboard entry by ID
#[tauri::command]
pub fn delete_clipboard_entry(app: tauri::AppHandle, id: String) -> Result<(), String> {
    let mut history = load_history(&app);
    history.entries.retain(|e| e.id != id);
    save_history(&app, &history)?;
    Ok(())
}

/// Toggle pin status of an entry
#[tauri::command]
pub fn toggle_pin_clipboard_entry(
    app: tauri::AppHandle,
    id: String,
) -> Result<ClipboardEntry, String> {
    let mut history = load_history(&app);
    let entry = history
        .entries
        .iter_mut()
        .find(|e| e.id == id)
        .ok_or("Entry not found")?;

    entry.is_pinned = !entry.is_pinned;
    let updated_entry = entry.clone();

    // Re-sort to move pinned entries to top
    history.entries.sort_by(|a, b| {
        if a.is_pinned && !b.is_pinned {
            std::cmp::Ordering::Less
        } else if !a.is_pinned && b.is_pinned {
            std::cmp::Ordering::Greater
        } else {
            b.timestamp.cmp(&a.timestamp)
        }
    });

    save_history(&app, &history)?;
    Ok(updated_entry)
}

/// Clear all non-pinned entries
#[tauri::command]
pub fn clear_clipboard_history(app: tauri::AppHandle) -> Result<(), String> {
    let mut history = load_history(&app);
    history.entries.retain(|e| e.is_pinned);
    save_history(&app, &history)?;
    Ok(())
}

/// Search clipboard entries by content
#[tauri::command]
pub fn search_clipboard_entries(
    app: tauri::AppHandle,
    query: String,
) -> Result<Vec<ClipboardEntry>, String> {
    let history = load_history(&app);
    let query_lower = query.to_lowercase();
    let entries = history
        .entries
        .into_iter()
        .filter(|e| {
            e.content.to_lowercase().contains(&query_lower)
                || e.language
                    .as_ref()
                    .is_some_and(|l| l.to_lowercase().contains(&query_lower))
                || e.source
                    .as_ref()
                    .is_some_and(|s| s.to_lowercase().contains(&query_lower))
        })
        .collect();
    Ok(entries)
}

/// Get all unique languages from history
#[tauri::command]
pub fn get_clipboard_languages(app: tauri::AppHandle) -> Result<Vec<String>, String> {
    let history = load_history(&app);
    let mut languages: Vec<String> = history
        .entries
        .iter()
        .filter_map(|e| e.language.clone())
        .collect();
    languages.sort();
    languages.dedup();
    Ok(languages)
}

/// Update the language of an entry
#[tauri::command]
pub fn update_clipboard_language(
    app: tauri::AppHandle,
    id: String,
    language: Option<String>,
) -> Result<ClipboardEntry, String> {
    let mut history = load_history(&app);
    let entry = history
        .entries
        .iter_mut()
        .find(|e| e.id == id)
        .ok_or("Entry not found")?;

    entry.language = language;
    let updated_entry = entry.clone();

    save_history(&app, &history)?;
    Ok(updated_entry)
}
