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

const QUICK_ACTIONS_STORE_KEY: &str = "quick_actions";

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QuickAction {
    pub id: String,
    pub name: String,
    pub prompt: String,
    pub icon: String,
    pub is_default: bool,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

fn get_default_quick_actions() -> Vec<QuickAction> {
    let now = Utc::now();
    vec![
        QuickAction {
            id: "default-review-pr".to_string(),
            name: "Review PR".to_string(),
            prompt: "Please review this pull request and provide feedback on code quality, potential issues, and suggestions for improvement.".to_string(),
            icon: "git-pull-request".to_string(),
            is_default: true,
            created_at: now,
            updated_at: now,
        },
        QuickAction {
            id: "default-run-tests".to_string(),
            name: "Run Tests".to_string(),
            prompt: "Please run the test suite for this project and report any failures or issues.".to_string(),
            icon: "play".to_string(),
            is_default: true,
            created_at: now,
            updated_at: now,
        },
        QuickAction {
            id: "default-explain-file".to_string(),
            name: "Explain File".to_string(),
            prompt: "Please explain what this file does, its purpose, and how it fits into the overall project structure.".to_string(),
            icon: "file-text".to_string(),
            is_default: true,
            created_at: now,
            updated_at: now,
        },
        QuickAction {
            id: "default-fix-error".to_string(),
            name: "Fix Error".to_string(),
            prompt: "I'm getting an error. Can you help me identify the cause and fix it?".to_string(),
            icon: "alert-circle".to_string(),
            is_default: true,
            created_at: now,
            updated_at: now,
        },
        QuickAction {
            id: "default-write-tests".to_string(),
            name: "Write Tests".to_string(),
            prompt: "Please write comprehensive unit tests for the current code with good coverage.".to_string(),
            icon: "check-square".to_string(),
            is_default: true,
            created_at: now,
            updated_at: now,
        },
        QuickAction {
            id: "default-refactor".to_string(),
            name: "Refactor".to_string(),
            prompt: "Please refactor this code to improve readability, maintainability, and performance.".to_string(),
            icon: "refresh-cw".to_string(),
            is_default: true,
            created_at: now,
            updated_at: now,
        },
    ]
}

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

    match store.get(QUICK_ACTIONS_STORE_KEY) {
        Some(value) => {
            let mut actions: Vec<QuickAction> =
                serde_json::from_value(value.clone()).map_err(|e| e.to_string())?;

            let defaults = get_default_quick_actions();
            for default in defaults {
                if !actions.iter().any(|a| a.id == default.id) {
                    actions.push(default);
                }
            }

            Ok(actions)
        }
        None => Ok(get_default_quick_actions()),
    }
}

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

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

    Ok(())
}

#[tauri::command]
pub async fn list_quick_actions(app: AppHandle) -> Result<Vec<QuickAction>, String> {
    let mut actions = load_all_quick_actions(&app)?;

    actions.sort_by(|a, b| {
        let default_cmp = b.is_default.cmp(&a.is_default);
        if default_cmp == std::cmp::Ordering::Equal {
            a.name.cmp(&b.name)
        } else {
            default_cmp
        }
    });

    Ok(actions)
}

#[tauri::command]
pub async fn save_quick_action(app: AppHandle, action: QuickAction) -> Result<(), String> {
    let mut actions = load_all_quick_actions(&app)?;

    if let Some(existing) = actions.iter_mut().find(|a| a.id == action.id) {
        let mut updated = action;
        updated.is_default = existing.is_default;
        *existing = updated;
    } else {
        actions.push(action);
    }

    save_all_quick_actions(&app, &actions)
}

#[tauri::command]
pub async fn delete_quick_action(app: AppHandle, action_id: String) -> Result<(), String> {
    let mut actions = load_all_quick_actions(&app)?;

    if actions
        .iter()
        .any(|a| a.id == action_id && a.is_default)
    {
        return Err("Cannot delete default quick actions".to_string());
    }

    actions.retain(|a| a.id != action_id);
    save_all_quick_actions(&app, &actions)
}

#[tauri::command]
pub async fn reset_default_quick_actions(app: AppHandle) -> Result<(), String> {
    let mut actions = load_all_quick_actions(&app)?;

    actions.retain(|a| !a.is_default);
    actions.extend(get_default_quick_actions());

    save_all_quick_actions(&app, &actions)
}

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

    fn create_test_action(id: &str, name: &str, is_default: bool) -> QuickAction {
        QuickAction {
            id: id.to_string(),
            name: name.to_string(),
            prompt: "Test prompt".to_string(),
            icon: "star".to_string(),
            is_default,
            created_at: Utc::now(),
            updated_at: Utc::now(),
        }
    }

    #[test]
    fn test_default_quick_actions_exist() {
        let defaults = get_default_quick_actions();
        assert!(!defaults.is_empty());
        assert!(defaults.iter().all(|a| a.is_default));
    }

    #[test]
    fn test_default_quick_actions_have_required_fields() {
        let defaults = get_default_quick_actions();
        for action in defaults {
            assert!(!action.id.is_empty());
            assert!(!action.name.is_empty());
            assert!(!action.prompt.is_empty());
            assert!(!action.icon.is_empty());
        }
    }

    #[test]
    fn test_default_quick_actions_count() {
        let defaults = get_default_quick_actions();
        // Should have 6 default actions
        assert_eq!(defaults.len(), 6);
    }

    #[test]
    fn test_default_quick_actions_have_unique_ids() {
        let defaults = get_default_quick_actions();
        let mut ids: Vec<&String> = defaults.iter().map(|a| &a.id).collect();
        ids.sort();
        ids.dedup();
        assert_eq!(ids.len(), defaults.len());
    }

    #[test]
    fn test_default_quick_actions_ids_start_with_default() {
        let defaults = get_default_quick_actions();
        assert!(defaults.iter().all(|a| a.id.starts_with("default-")));
    }

    #[test]
    fn test_quick_action_serialization() {
        let action = create_test_action("test-1", "Test Action", false);
        let json = serde_json::to_string(&action).expect("Failed to serialize");
        let parsed: QuickAction = serde_json::from_str(&json).expect("Failed to deserialize");

        assert_eq!(parsed.id, action.id);
        assert_eq!(parsed.name, action.name);
        assert_eq!(parsed.prompt, action.prompt);
        assert_eq!(parsed.icon, action.icon);
        assert_eq!(parsed.is_default, action.is_default);
    }

    #[test]
    fn test_quick_action_clone() {
        let original = create_test_action("clone-test", "Clone Test", true);
        let cloned = original.clone();

        assert_eq!(original.id, cloned.id);
        assert_eq!(original.name, cloned.name);
        assert_eq!(original.is_default, cloned.is_default);
    }

    #[test]
    #[allow(clippy::useless_vec)]
    fn test_quick_action_sorting_defaults_first() {
        let mut actions = vec![
            create_test_action("custom-z", "Zebra", false),
            create_test_action("default-a", "Apple", true),
            create_test_action("custom-a", "Alpha", false),
            create_test_action("default-z", "Zulu", true),
        ];

        // Sort by: defaults first, then alphabetically by name
        actions.sort_by(|a, b| {
            let default_cmp = b.is_default.cmp(&a.is_default);
            if default_cmp == std::cmp::Ordering::Equal {
                a.name.cmp(&b.name)
            } else {
                default_cmp
            }
        });

        // Defaults should come first
        assert!(actions[0].is_default);
        assert!(actions[1].is_default);
        assert!(!actions[2].is_default);
        assert!(!actions[3].is_default);

        // Within defaults, alphabetically sorted
        assert_eq!(actions[0].name, "Apple");
        assert_eq!(actions[1].name, "Zulu");

        // Within non-defaults, alphabetically sorted
        assert_eq!(actions[2].name, "Alpha");
        assert_eq!(actions[3].name, "Zebra");
    }

    #[test]
    fn test_known_default_actions() {
        let defaults = get_default_quick_actions();
        let ids: Vec<&str> = defaults.iter().map(|a| a.id.as_str()).collect();

        assert!(ids.contains(&"default-review-pr"));
        assert!(ids.contains(&"default-run-tests"));
        assert!(ids.contains(&"default-explain-file"));
        assert!(ids.contains(&"default-fix-error"));
        assert!(ids.contains(&"default-write-tests"));
        assert!(ids.contains(&"default-refactor"));
    }

    #[test]
    fn test_default_action_icons() {
        let defaults = get_default_quick_actions();
        let icons: Vec<&str> = defaults.iter().map(|a| a.icon.as_str()).collect();

        assert!(icons.contains(&"git-pull-request"));
        assert!(icons.contains(&"play"));
        assert!(icons.contains(&"file-text"));
        assert!(icons.contains(&"alert-circle"));
        assert!(icons.contains(&"check-square"));
        assert!(icons.contains(&"refresh-cw"));
    }

    #[test]
    fn test_quick_action_prompts_not_empty() {
        let defaults = get_default_quick_actions();
        for action in defaults {
            assert!(
                action.prompt.len() > 10,
                "Prompt should be meaningful: {}",
                action.name
            );
        }
    }

    #[test]
    fn test_quick_action_timestamps() {
        let action = create_test_action("time-test", "Time Test", false);
        assert!(action.created_at <= action.updated_at);
    }

    #[test]
    fn test_default_actions_have_same_timestamps() {
        let defaults = get_default_quick_actions();
        // All defaults are created at the same instant
        let first_created = defaults[0].created_at;
        let first_updated = defaults[0].updated_at;

        for action in &defaults {
            assert_eq!(action.created_at, first_created);
            assert_eq!(action.updated_at, first_updated);
        }
    }

    #[test]
    fn test_action_retain_non_default() {
        let mut actions = vec![
            create_test_action("default-1", "Default 1", true),
            create_test_action("custom-1", "Custom 1", false),
            create_test_action("default-2", "Default 2", true),
            create_test_action("custom-2", "Custom 2", false),
        ];

        // Mimics reset_default_quick_actions behavior (retain non-defaults)
        actions.retain(|a| !a.is_default);

        assert_eq!(actions.len(), 2);
        assert!(actions.iter().all(|a| !a.is_default));
    }

    #[test]
    #[allow(clippy::useless_vec)]
    fn test_action_find_by_id() {
        let actions = vec![
            create_test_action("action-1", "First", false),
            create_test_action("action-2", "Second", false),
            create_test_action("action-3", "Third", false),
        ];

        let found = actions.iter().find(|a| a.id == "action-2");
        assert!(found.is_some());
        assert_eq!(found.unwrap().name, "Second");

        let not_found = actions.iter().find(|a| a.id == "action-999");
        assert!(not_found.is_none());
    }
}
