use pixstrip_core::preset::Preset;
use pixstrip_core::storage::*;

fn with_temp_config_dir(f: impl FnOnce(&std::path::Path)) {
    let dir = tempfile::tempdir().unwrap();
    f(dir.path());
}

// --- Preset file I/O ---

#[test]
fn save_and_load_preset() {
    with_temp_config_dir(|base| {
        let store = PresetStore::with_base_dir(base);
        let preset = Preset::builtin_blog_photos();

        store.save(&preset).unwrap();
        let loaded = store.load("Blog Photos").unwrap();

        assert_eq!(loaded.name, preset.name);
        assert_eq!(loaded.is_custom, preset.is_custom);
    });
}

#[test]
fn save_preset_creates_directory() {
    with_temp_config_dir(|base| {
        let presets_dir = base.join("presets");
        assert!(!presets_dir.exists());

        let store = PresetStore::with_base_dir(base);
        let preset = Preset::builtin_blog_photos();
        store.save(&preset).unwrap();

        assert!(presets_dir.exists());
    });
}

#[test]
fn list_saved_presets() {
    with_temp_config_dir(|base| {
        let store = PresetStore::with_base_dir(base);
        store.save(&Preset::builtin_blog_photos()).unwrap();
        store.save(&Preset::builtin_privacy_clean()).unwrap();

        let list = store.list().unwrap();
        assert_eq!(list.len(), 2);

        let names: Vec<&str> = list.iter().map(|p| p.name.as_str()).collect();
        assert!(names.contains(&"Blog Photos"));
        assert!(names.contains(&"Privacy Clean"));
    });
}

#[test]
fn delete_preset() {
    with_temp_config_dir(|base| {
        let store = PresetStore::with_base_dir(base);
        store.save(&Preset::builtin_blog_photos()).unwrap();

        assert!(store.delete("Blog Photos").is_ok());
        assert!(store.load("Blog Photos").is_err());
    });
}

#[test]
fn load_nonexistent_preset_returns_error() {
    with_temp_config_dir(|base| {
        let store = PresetStore::with_base_dir(base);
        assert!(store.load("Nonexistent").is_err());
    });
}

#[test]
fn preset_filename_sanitization() {
    with_temp_config_dir(|base| {
        let store = PresetStore::with_base_dir(base);
        let mut preset = Preset::builtin_blog_photos();
        preset.name = "My Preset / With <Special> Chars!".into();
        preset.is_custom = true;

        store.save(&preset).unwrap();
        let loaded = store.load("My Preset / With <Special> Chars!").unwrap();
        assert_eq!(loaded.name, preset.name);
    });
}

// --- Preset import/export ---

#[test]
fn export_and_import_preset() {
    with_temp_config_dir(|base| {
        let store = PresetStore::with_base_dir(base);
        let preset = Preset::builtin_blog_photos();

        let export_path = base.join("exported.pixstrip-preset");
        store.export_to_file(&preset, &export_path).unwrap();

        assert!(export_path.exists());

        let imported = store.import_from_file(&export_path).unwrap();
        assert_eq!(imported.name, preset.name);
        assert!(imported.is_custom);
    });
}

#[test]
fn import_preset_marks_as_custom() {
    with_temp_config_dir(|base| {
        let store = PresetStore::with_base_dir(base);
        let preset = Preset::builtin_blog_photos();

        let export_path = base.join("test.pixstrip-preset");
        store.export_to_file(&preset, &export_path).unwrap();

        let imported = store.import_from_file(&export_path).unwrap();
        assert!(imported.is_custom);
    });
}

// --- Config persistence ---

#[test]
fn save_and_load_config() {
    with_temp_config_dir(|base| {
        let config_store = ConfigStore::with_base_dir(base);
        let config = pixstrip_core::config::AppConfig {
            output_subfolder: "output_images".into(),
            auto_open_output: true,
            ..Default::default()
        };

        config_store.save(&config).unwrap();
        let loaded = config_store.load().unwrap();

        assert_eq!(loaded.output_subfolder, "output_images");
        assert!(loaded.auto_open_output);
    });
}

#[test]
fn load_missing_config_returns_default() {
    with_temp_config_dir(|base| {
        let config_store = ConfigStore::with_base_dir(base);
        let config = config_store.load().unwrap();
        let default = pixstrip_core::config::AppConfig::default();

        assert_eq!(config.output_subfolder, default.output_subfolder);
    });
}

// --- Session memory ---

#[test]
fn save_and_load_session() {
    with_temp_config_dir(|base| {
        let session_store = SessionStore::with_base_dir(base);
        let session = SessionState {
            last_input_dir: Some("/home/user/photos".into()),
            last_output_dir: Some("/home/user/processed".into()),
            last_preset_name: Some("Blog Photos".into()),
            current_step: 3,
            window_width: Some(1024),
            window_height: Some(768),
            window_maximized: false,
            ..Default::default()
        };

        session_store.save(&session).unwrap();
        let loaded = session_store.load().unwrap();

        assert_eq!(loaded.last_input_dir.as_deref(), Some("/home/user/photos"));
        assert_eq!(loaded.current_step, 3);
    });
}

#[test]
fn load_missing_session_returns_default() {
    with_temp_config_dir(|base| {
        let session_store = SessionStore::with_base_dir(base);
        let session = session_store.load().unwrap();
        assert_eq!(session.current_step, 0);
        assert!(session.last_input_dir.is_none());
    });
}

// --- Processing history ---

#[test]
fn add_and_list_history_entries() {
    with_temp_config_dir(|base| {
        let history = HistoryStore::with_base_dir(base);

        let entry = HistoryEntry {
            timestamp: "2026-03-06T12:00:00Z".into(),
            input_dir: "/home/user/photos".into(),
            output_dir: "/home/user/processed".into(),
            preset_name: Some("Blog Photos".into()),
            total: 10,
            succeeded: 9,
            failed: 1,
            total_input_bytes: 50_000_000,
            total_output_bytes: 10_000_000,
            elapsed_ms: 5000,
            output_files: vec![
                "/home/user/processed/photo1.jpg".into(),
                "/home/user/processed/photo2.jpg".into(),
            ],
        };

        history.add(entry.clone(), 50, 30).unwrap();
        let entries = history.list().unwrap();

        assert_eq!(entries.len(), 1);
        assert_eq!(entries[0].total, 10);
        assert_eq!(entries[0].succeeded, 9);
    });
}

#[test]
fn history_appends_entries() {
    with_temp_config_dir(|base| {
        let history = HistoryStore::with_base_dir(base);

        for i in 0..3 {
            history
                .add(HistoryEntry {
                    timestamp: format!("2026-03-06T12:0{}:00Z", i),
                    input_dir: "/input".into(),
                    output_dir: "/output".into(),
                    preset_name: None,
                    total: i + 1,
                    succeeded: i + 1,
                    failed: 0,
                    total_input_bytes: 1000,
                    total_output_bytes: 500,
                    elapsed_ms: 100,
                    output_files: vec![],
                }, 50, 30)
                .unwrap();
        }

        let entries = history.list().unwrap();
        assert_eq!(entries.len(), 3);
    });
}

#[test]
fn clear_history() {
    with_temp_config_dir(|base| {
        let history = HistoryStore::with_base_dir(base);

        history
            .add(HistoryEntry {
                timestamp: "2026-03-06T12:00:00Z".into(),
                input_dir: "/input".into(),
                output_dir: "/output".into(),
                preset_name: None,
                total: 1,
                succeeded: 1,
                failed: 0,
                total_input_bytes: 1000,
                total_output_bytes: 500,
                elapsed_ms: 100,
                output_files: vec![],
            }, 50, 30)
            .unwrap();

        history.clear().unwrap();
        let entries = history.list().unwrap();
        assert!(entries.is_empty());
    });
}

#[test]
fn empty_history_returns_empty_list() {
    with_temp_config_dir(|base| {
        let history = HistoryStore::with_base_dir(base);
        let entries = history.list().unwrap();
        assert!(entries.is_empty());
    });
}
