use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use tauri::AppHandle;
use tauri_plugin_store::StoreExt;

const SESSIONS_STORE_KEY: &str = "sessions";

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SavedSession {
    pub id: String,
    pub name: String,
    pub created_at: DateTime<Utc>,
    pub last_activity_at: DateTime<Utc>,
    pub working_directory: String,
    pub message_count: usize,
    pub preview: String, // First ~100 chars of conversation for preview
    pub messages: Vec<SavedMessage>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SavedMessage {
    pub id: String,
    #[serde(rename = "type")]
    pub message_type: String,
    pub content: String,
    pub timestamp: DateTime<Utc>,
    pub tool_name: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionListItem {
    pub id: String,
    pub name: String,
    pub created_at: DateTime<Utc>,
    pub last_activity_at: DateTime<Utc>,
    pub working_directory: String,
    pub message_count: usize,
    pub preview: String,
}

impl From<&SavedSession> for SessionListItem {
    fn from(session: &SavedSession) -> Self {
        SessionListItem {
            id: session.id.clone(),
            name: session.name.clone(),
            created_at: session.created_at,
            last_activity_at: session.last_activity_at,
            working_directory: session.working_directory.clone(),
            message_count: session.message_count,
            preview: session.preview.clone(),
        }
    }
}

fn load_all_sessions(app: &AppHandle) -> Result<Vec<SavedSession>, String> {
    let store = app
        .store("hikari-sessions.json")
        .map_err(|e| e.to_string())?;

    match store.get(SESSIONS_STORE_KEY) {
        Some(value) => serde_json::from_value(value.clone()).map_err(|e| e.to_string()),
        None => Ok(Vec::new()),
    }
}

fn save_all_sessions(app: &AppHandle, sessions: &[SavedSession]) -> Result<(), String> {
    let store = app
        .store("hikari-sessions.json")
        .map_err(|e| e.to_string())?;

    let value = serde_json::to_value(sessions).map_err(|e| e.to_string())?;
    store.set(SESSIONS_STORE_KEY, value);
    store.save().map_err(|e| e.to_string())?;

    Ok(())
}

#[tauri::command]
pub async fn list_sessions(app: AppHandle) -> Result<Vec<SessionListItem>, String> {
    let sessions = load_all_sessions(&app)?;
    let mut items: Vec<SessionListItem> = sessions.iter().map(SessionListItem::from).collect();

    // Sort by last activity, most recent first
    items.sort_by(|a, b| b.last_activity_at.cmp(&a.last_activity_at));

    Ok(items)
}

#[tauri::command]
pub async fn save_session(app: AppHandle, session: SavedSession) -> Result<(), String> {
    let mut sessions = load_all_sessions(&app)?;

    // Update existing or add new
    if let Some(existing) = sessions.iter_mut().find(|s| s.id == session.id) {
        *existing = session;
    } else {
        sessions.push(session);
    }

    save_all_sessions(&app, &sessions)
}

#[tauri::command]
pub async fn load_session(app: AppHandle, session_id: String) -> Result<Option<SavedSession>, String> {
    let sessions = load_all_sessions(&app)?;
    Ok(sessions.into_iter().find(|s| s.id == session_id))
}

#[tauri::command]
pub async fn delete_session(app: AppHandle, session_id: String) -> Result<(), String> {
    let mut sessions = load_all_sessions(&app)?;
    sessions.retain(|s| s.id != session_id);
    save_all_sessions(&app, &sessions)
}

#[tauri::command]
pub async fn search_sessions(app: AppHandle, query: String) -> Result<Vec<SessionListItem>, String> {
    let sessions = load_all_sessions(&app)?;
    let query_lower = query.to_lowercase();

    let mut matching: Vec<SessionListItem> = sessions
        .iter()
        .filter(|s| {
            s.name.to_lowercase().contains(&query_lower)
                || s.preview.to_lowercase().contains(&query_lower)
                || s.working_directory.to_lowercase().contains(&query_lower)
                || s.messages
                    .iter()
                    .any(|m| m.content.to_lowercase().contains(&query_lower))
        })
        .map(SessionListItem::from)
        .collect();

    // Sort by last activity, most recent first
    matching.sort_by(|a, b| b.last_activity_at.cmp(&a.last_activity_at));

    Ok(matching)
}

#[tauri::command]
pub async fn clear_all_sessions(app: AppHandle) -> Result<(), String> {
    save_all_sessions(&app, &[])
}

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

    #[test]
    fn test_session_list_item_from_saved_session() {
        let session = SavedSession {
            id: "test-id".to_string(),
            name: "Test Session".to_string(),
            created_at: Utc::now(),
            last_activity_at: Utc::now(),
            working_directory: "/home/test".to_string(),
            message_count: 5,
            preview: "Hello world".to_string(),
            messages: vec![],
        };

        let item = SessionListItem::from(&session);
        assert_eq!(item.id, "test-id");
        assert_eq!(item.name, "Test Session");
        assert_eq!(item.message_count, 5);
    }
}
