use std::path::PathBuf;

use tempdir::TempDir;
use try_rs::config::*;
use try_rs::themes::Theme;

#[test]
fn save_and_reload_config() {
    let tmp = TempDir::new("save-config-test").unwrap();
    let config_path = tmp.path().join("config.toml");
    let theme = Theme::default();
    let tries_path = PathBuf::from("/tmp/tries");

save_config(
        &config_path,
        &theme,
        &[tries_path],
        &Some("code".to_string()),
        Some(true),
        None,
        Some(false),
        Some(false),
        Some(true),
        Some(false),
        Some(true),
        Some(40),
    )
    .unwrap();

    let contents = std::fs::read_to_string(&config_path).unwrap();
    let loaded: Config = toml::from_str(&contents).unwrap();
    assert_eq!(loaded.tries_path.as_deref(), Some("/tmp/tries"));
    assert_eq!(loaded.theme.as_deref(), Some("Default"));
    assert_eq!(loaded.editor.as_deref(), Some("code"));
    assert_eq!(loaded.apply_date_prefix, Some(true));
    assert_eq!(loaded.transparent_background, Some(false));
    assert_eq!(loaded.show_disk, Some(false));
    assert_eq!(loaded.show_preview, Some(true));
    assert_eq!(loaded.show_legend, Some(false));
    assert_eq!(loaded.show_right_panel, Some(true));
    assert_eq!(loaded.right_panel_width, Some(40));
}

#[test]
fn save_config_creates_parent_dirs() {
    let tmp = TempDir::new("save-nested").unwrap();
    let config_path = tmp.path().join("nested").join("dir").join("config.toml");
    let theme = Theme::default();

    save_config(
        &config_path,
        &theme,
        &[PathBuf::from("/tmp/t")],
        &None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
    )
    .unwrap();

    assert!(config_path.exists());
}

#[test]
fn save_config_none_optionals() {
    let tmp = TempDir::new("save-none").unwrap();
    let config_path = tmp.path().join("config.toml");
    let theme = Theme::default();

    save_config(
        &config_path,
        &theme,
        &[PathBuf::from("/tmp/t")],
        &None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
    )
    .unwrap();

    let contents = std::fs::read_to_string(&config_path).unwrap();
    let loaded: Config = toml::from_str(&contents).unwrap();
    assert!(loaded.editor.is_none());
    assert!(loaded.apply_date_prefix.is_none());
    assert!(loaded.transparent_background.is_none());
    assert!(loaded.show_disk.is_none());
    assert!(loaded.show_preview.is_none());
    assert!(loaded.show_legend.is_none());
    assert!(loaded.show_right_panel.is_none());
    assert!(loaded.right_panel_width.is_none());
}

#[test]
fn config_serialization_roundtrip() {
    let config = Config {
        tries_path: Some("~/work/tries".to_string()),
        tries_paths: None,
        theme: Some("Tokyo Night".to_string()),
        editor: Some("nvim".to_string()),
        apply_date_prefix: Some(true),
        date_prefix_format: None,
        transparent_background: Some(true),
        show_disk: Some(false),
        show_preview: Some(true),
        show_legend: Some(false),
        show_right_panel: Some(true),
        right_panel_width: Some(25),
    };

    let toml_str = toml::to_string(&config).unwrap();
    let loaded: Config = toml::from_str(&toml_str).unwrap();

    assert_eq!(loaded.tries_path, config.tries_path);
    assert_eq!(loaded.theme, config.theme);
    assert_eq!(loaded.editor, config.editor);
    assert_eq!(loaded.apply_date_prefix, config.apply_date_prefix);
    assert_eq!(loaded.transparent_background, config.transparent_background);
    assert_eq!(loaded.show_disk, config.show_disk);
    assert_eq!(loaded.show_preview, config.show_preview);
    assert_eq!(loaded.show_legend, config.show_legend);
    assert_eq!(loaded.show_right_panel, config.show_right_panel);
    assert_eq!(loaded.right_panel_width, config.right_panel_width);
}

#[test]
fn config_deserialize_empty() {
    let config: Config = toml::from_str("").unwrap();
    assert!(config.tries_path.is_none());
    assert!(config.theme.is_none());
    assert!(config.editor.is_none());
    assert!(config.apply_date_prefix.is_none());
    assert!(config.transparent_background.is_none());
    assert!(config.show_disk.is_none());
    assert!(config.show_preview.is_none());
    assert!(config.show_legend.is_none());
    assert!(config.show_right_panel.is_none());
    assert!(config.right_panel_width.is_none());
}

#[test]
fn config_deserialize_partial() {
    let config: Config = toml::from_str(r#"theme = "Nord""#).unwrap();
    assert_eq!(config.theme.as_deref(), Some("Nord"));
    assert!(config.tries_path.is_none());
}

#[test]
fn config_ignores_unknown_fields() {
    let result: Result<Config, _> = toml::from_str(
        r#"
theme = "Default"
unknown_field = "value"
"#,
    );
    let _ = result;
}

#[test]
fn get_file_config_toml_name_default() {
    unsafe { std::env::remove_var("TRY_CONFIG") };
    assert_eq!(get_file_config_toml_name(), "config.toml");
}

#[test]
fn get_config_dir_with_env() {
    unsafe { std::env::set_var("TRY_CONFIG_DIR", "/custom/config/dir") };
    let dir = get_config_dir();
    assert_eq!(dir, PathBuf::from("/custom/config/dir"));
    unsafe { std::env::remove_var("TRY_CONFIG_DIR") };
}

#[test]
fn save_config_preserves_theme_name() {
    let tmp = TempDir::new("theme-name").unwrap();
    let config_path = tmp.path().join("config.toml");

    let themes = Theme::all();
    for theme in &themes {
        save_config(
            &config_path,
            theme,
            &[PathBuf::from("/tmp/t")],
            &None,
            None,
            None,
            None,
            None,
            None,
            None,
            None,
            None,
        )
        .unwrap();

        let contents = std::fs::read_to_string(&config_path).unwrap();
        let loaded: Config = toml::from_str(&contents).unwrap();
        assert_eq!(loaded.theme.as_deref(), Some(theme.name.as_str()));
    }
}

#[test]
fn config_with_all_fields() {
    let toml_str = r#"
tries_path = "/home/user/tries"
theme = "Tokyo Night"
editor = "vim"
apply_date_prefix = true
transparent_background = false
show_disk = false
show_preview = true
show_legend = false
show_right_panel = true
right_panel_width = 44
"#;

    let config: Config = toml::from_str(toml_str).unwrap();
    assert_eq!(config.tries_path, Some("/home/user/tries".to_string()));
    assert_eq!(config.theme, Some("Tokyo Night".to_string()));
    assert_eq!(config.editor, Some("vim".to_string()));
    assert_eq!(config.apply_date_prefix, Some(true));
    assert_eq!(config.transparent_background, Some(false));
    assert_eq!(config.show_disk, Some(false));
    assert_eq!(config.show_preview, Some(true));
    assert_eq!(config.show_legend, Some(false));
    assert_eq!(config.show_right_panel, Some(true));
    assert_eq!(config.right_panel_width, Some(44));
}

#[test]
fn config_save_and_load_roundtrip() {
    let tmp = TempDir::new("config-roundtrip").unwrap();
    let config_path = tmp.path().join("test.toml");
    let theme = Theme::default();

    save_config(
        &config_path,
        &theme,
        &[PathBuf::from("~/tries")],
        &Some("nvim".to_string()),
        Some(true),
        None,
        Some(true),
        Some(false),
        Some(true),
        Some(true),
        Some(false),
        Some(52),
    )
    .unwrap();

    let contents = std::fs::read_to_string(&config_path).unwrap();
    let loaded: Config = toml::from_str(&contents).unwrap();

    assert_eq!(loaded.tries_path, Some("~/tries".to_string()));
    assert_eq!(loaded.editor, Some("nvim".to_string()));
    assert_eq!(loaded.apply_date_prefix, Some(true));
    assert_eq!(loaded.transparent_background, Some(true));
    assert_eq!(loaded.show_disk, Some(false));
    assert_eq!(loaded.show_preview, Some(true));
    assert_eq!(loaded.show_legend, Some(true));
    assert_eq!(loaded.show_right_panel, Some(false));
    assert_eq!(loaded.right_panel_width, Some(52));
}

#[test]
fn config_handles_whitespace_in_values() {
    let toml_str = r#"
tries_path = " /home/user/path with spaces "
editor = " code --wait "
"#;

    let config: Config = toml::from_str(toml_str).unwrap();
    assert_eq!(
        config.tries_path,
        Some(" /home/user/path with spaces ".to_string())
    );
    assert_eq!(config.editor, Some(" code --wait ".to_string()));
}

#[test]
fn config_handles_unicode() {
    let toml_str = r#"
tries_path = "/home/user/实验"
theme = "东京之夜"
"#;

    let config: Config = toml::from_str(toml_str).unwrap();
    assert_eq!(config.tries_path, Some("/home/user/实验".to_string()));
    assert_eq!(config.theme, Some("东京之夜".to_string()));
}

#[test]
fn config_preserve_booleans_exact() {
    let config1 = Config {
        tries_path: None,
        tries_paths: None,
        theme: None,
        editor: None,
        apply_date_prefix: Some(true),
        date_prefix_format: None,
        transparent_background: Some(false),
        show_disk: Some(false),
        show_preview: Some(true),
        show_legend: Some(false),
        show_right_panel: Some(true),
        right_panel_width: Some(41),
    };

    let toml = toml::to_string(&config1).unwrap();
    let config2: Config = toml::from_str(&toml).unwrap();

    assert_eq!(config1.apply_date_prefix, config2.apply_date_prefix);
    assert_eq!(
        config1.transparent_background,
        config2.transparent_background
    );
    assert_eq!(config1.show_disk, config2.show_disk);
    assert_eq!(config1.show_preview, config2.show_preview);
    assert_eq!(config1.show_legend, config2.show_legend);
    assert_eq!(config1.show_right_panel, config2.show_right_panel);
    assert_eq!(config1.right_panel_width, config2.right_panel_width);
}

#[test]
fn config_with_empty_strings() {
    let config = Config {
        tries_path: Some("".to_string()),
        tries_paths: None,
        theme: Some("".to_string()),
        editor: Some("".to_string()),
        apply_date_prefix: None,
        date_prefix_format: None,
        transparent_background: None,
        show_disk: None,
        show_preview: None,
        show_legend: None,
        show_right_panel: None,
        right_panel_width: None,
    };

    let toml = toml::to_string(&config).unwrap();
    let loaded: Config = toml::from_str(&toml).unwrap();

    assert_eq!(loaded.tries_path, Some("".to_string()));
    assert_eq!(loaded.theme, Some("".to_string()));
    assert_eq!(loaded.editor, Some("".to_string()));
}

#[test]
fn config_save_overwrites_existing() {
    let tmp = TempDir::new("config-overwrite").unwrap();
    let config_path = tmp.path().join("config.toml");

    let theme1 = Theme::default();
    save_config(
        &config_path,
        &theme1,
        &[PathBuf::from("/path1")],
        &Some("editor1".to_string()),
        Some(true),
        None,
        None,
        None,
        None,
        None,
        None,
        None,
    )
    .unwrap();

    let theme2 = Theme::tokyo_night();
    save_config(
        &config_path,
        &theme2,
        &[PathBuf::from("/path2")],
        &Some("editor2".to_string()),
        Some(false),
        None,
        Some(true),
        Some(true),
        Some(false),
        Some(true),
        Some(false),
        Some(63),
    )
    .unwrap();

    let contents = std::fs::read_to_string(&config_path).unwrap();
    let loaded: Config = toml::from_str(&contents).unwrap();

    assert_eq!(loaded.tries_path, Some("/path2".to_string()));
    assert_eq!(loaded.editor, Some("editor2".to_string()));
    assert_eq!(loaded.apply_date_prefix, Some(false));
    assert_eq!(loaded.transparent_background, Some(true));
    assert_eq!(loaded.show_disk, Some(true));
    assert_eq!(loaded.show_preview, Some(false));
    assert_eq!(loaded.show_legend, Some(true));
    assert_eq!(loaded.show_right_panel, Some(false));
    assert_eq!(loaded.right_panel_width, Some(63));
    assert_eq!(loaded.theme, Some("Tokyo Night".to_string()));
}

#[test]
fn config_serialization_order() {
    let config = Config {
        theme: Some("Default".to_string()),
        tries_path: Some("/path".to_string()),
        tries_paths: None,
        editor: Some("vim".to_string()),
        apply_date_prefix: Some(true),
        date_prefix_format: None,
        transparent_background: Some(false),
        show_disk: Some(false),
        show_preview: Some(true),
        show_legend: Some(false),
        show_right_panel: Some(true),
        right_panel_width: Some(37),
    };

    let toml = toml::to_string(&config).unwrap();

    // All fields should be present
    assert!(toml.contains("theme"));
    assert!(toml.contains("tries_path"));
    assert!(toml.contains("editor"));
    assert!(toml.contains("apply_date_prefix"));
    assert!(toml.contains("transparent_background"));
    assert!(toml.contains("show_disk"));
    assert!(toml.contains("show_preview"));
    assert!(toml.contains("show_legend"));
    assert!(toml.contains("show_right_panel"));
    assert!(toml.contains("right_panel_width"));
}

#[test]
fn config_handles_comments_in_toml() {
    let toml_str = r#"
# This is a comment
tries_path = "/home/user/tries"
# Another comment
theme = "Default"
"#;

    let config: Config = toml::from_str(toml_str).unwrap();
    assert_eq!(config.tries_path, Some("/home/user/tries".to_string()));
    assert_eq!(config.theme, Some("Default".to_string()));
}

#[test]
fn config_file_extension_handling() {
    unsafe {
        std::env::remove_var("TRY_CONFIG");
    }

    let name = try_rs::config::get_file_config_toml_name();
    assert_eq!(name, "config.toml");

    unsafe {
        std::env::set_var("TRY_CONFIG", "custom.toml");
    }
    let name = try_rs::config::get_file_config_toml_name();
    assert_eq!(name, "custom.toml");

    unsafe {
        std::env::remove_var("TRY_CONFIG");
    }
}

#[test]
fn config_handles_special_characters_in_paths() {
    let toml_str = r#"
tries_path = "/path/with-dash/and_underscore/and.dot"
"#;

    let config: Config = toml::from_str(toml_str).unwrap();
    assert_eq!(
        config.tries_path,
        Some("/path/with-dash/and_underscore/and.dot".to_string())
    );
}

#[test]
fn config_handles_very_long_values() {
    let long_path = "/very".repeat(100);
    let config = Config {
        tries_path: Some(long_path.clone()),
        tries_paths: None,
        theme: None,
        editor: None,
        apply_date_prefix: None,
        date_prefix_format: None,
        transparent_background: None,
        show_disk: None,
        show_preview: None,
        show_legend: None,
        show_right_panel: None,
        right_panel_width: None,
    };

    let toml = toml::to_string(&config).unwrap();
    let loaded: Config = toml::from_str(&toml).unwrap();

    assert_eq!(loaded.tries_path, Some(long_path));
}
