use anyhow::Result;
use serde_json::Value;
use settings_content::{PlatformOverrides, ReleaseChannelOverrides};

/// Applies a migration callback to the root settings object as well as all
/// nested platform, release-channel, and profile override objects.
pub(crate) fn migrate_settings(
    value: &mut Value,
    migrate_one: &mut dyn FnMut(&mut serde_json::Map<String, Value>) -> Result<()>,
) -> Result<()> {
    let Some(root_object) = value.as_object_mut() else {
        return Ok(());
    };

    migrate_one(root_object)?;

    let override_keys = ReleaseChannelOverrides::OVERRIDE_KEYS
        .iter()
        .copied()
        .chain(PlatformOverrides::OVERRIDE_KEYS.iter().copied());

    for key in override_keys {
        if let Some(sub_object) = root_object.get_mut(key) {
            if let Some(sub_map) = sub_object.as_object_mut() {
                migrate_one(sub_map)?;
            }
        }
    }

    if let Some(profiles) = root_object.get_mut("profiles") {
        if let Some(profiles_object) = profiles.as_object_mut() {
            for profile_value in profiles_object.values_mut() {
                if let Some(profile_map) = profile_value.as_object_mut() {
                    if let Some(inner) = profile_map
                        .get_mut("settings")
                        .and_then(|v| v.as_object_mut())
                    {
                        migrate_one(inner)?;
                    } else {
                        migrate_one(profile_map)?;
                    }
                }
            }
        }
    }

    Ok(())
}

/// Applies a migration callback to a value and its `languages` children,
/// at the root level as well as all nested platform, release-channel, and
/// profile override objects.
pub(crate) fn migrate_language_setting(
    value: &mut Value,
    migrate_fn: fn(&mut Value, path: &[&str]) -> Result<()>,
) -> Result<()> {
    fn apply_to_value_and_languages(
        value: &mut Value,
        prefix: &[&str],
        migrate_fn: fn(&mut Value, path: &[&str]) -> Result<()>,
    ) -> Result<()> {
        migrate_fn(value, prefix)?;
        let languages = value
            .as_object_mut()
            .and_then(|obj| obj.get_mut("languages"))
            .and_then(|languages| languages.as_object_mut());
        if let Some(languages) = languages {
            for (language_name, language) in languages.iter_mut() {
                let mut path: Vec<&str> = prefix.to_vec();
                path.push("languages");
                path.push(language_name);
                migrate_fn(language, &path)?;
            }
        }
        Ok(())
    }

    if !value.is_object() {
        return Ok(());
    }

    apply_to_value_and_languages(value, &[], migrate_fn)?;

    let Some(root_object) = value.as_object_mut() else {
        return Ok(());
    };

    let override_keys = ReleaseChannelOverrides::OVERRIDE_KEYS
        .iter()
        .copied()
        .chain(PlatformOverrides::OVERRIDE_KEYS.iter().copied());

    for key in override_keys {
        if let Some(sub_value) = root_object.get_mut(key) {
            apply_to_value_and_languages(sub_value, &[key], migrate_fn)?;
        }
    }

    if let Some(profiles) = root_object.get_mut("profiles") {
        if let Some(profiles_object) = profiles.as_object_mut() {
            let profile_names: Vec<String> = profiles_object.keys().cloned().collect();
            for profile_name in &profile_names {
                if let Some(profile_value) = profiles_object.get_mut(profile_name.as_str()) {
                    if let Some(settings_value) = profile_value
                        .as_object_mut()
                        .and_then(|m| m.get_mut("settings"))
                    {
                        apply_to_value_and_languages(
                            settings_value,
                            &["profiles", profile_name],
                            migrate_fn,
                        )?;
                    } else {
                        apply_to_value_and_languages(
                            profile_value,
                            &["profiles", profile_name],
                            migrate_fn,
                        )?;
                    }
                }
            }
        }
    }

    Ok(())
}

pub(crate) mod m_2025_01_02 {
    mod settings;

    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2025_01_29 {
    mod keymap;
    mod settings;

    pub(crate) use keymap::KEYMAP_PATTERNS;
    pub(crate) use settings::{SETTINGS_PATTERNS, replace_edit_prediction_provider_setting};
}

pub(crate) mod m_2025_01_30 {
    mod keymap;
    mod settings;

    pub(crate) use keymap::KEYMAP_PATTERNS;
    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2025_03_03 {
    mod keymap;

    pub(crate) use keymap::KEYMAP_PATTERNS;
}

pub(crate) mod m_2025_03_06 {
    mod keymap;

    pub(crate) use keymap::KEYMAP_PATTERNS;
}

pub(crate) mod m_2025_03_29 {
    mod settings;

    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2025_04_15 {
    mod keymap;
    mod settings;

    pub(crate) use keymap::KEYMAP_PATTERNS;
    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2025_04_21 {
    mod settings;

    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2025_04_23 {
    mod settings;

    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2025_05_05 {
    mod settings;

    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2025_05_08 {
    mod settings;

    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2025_06_16 {
    mod settings;

    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2025_06_25 {
    mod settings;

    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2025_06_27 {
    mod settings;

    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2025_07_08 {
    mod settings;

    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2025_10_01 {
    mod settings;

    pub(crate) use settings::flatten_code_actions_formatters;
}

pub(crate) mod m_2025_10_02 {
    mod settings;

    pub(crate) use settings::remove_formatters_on_save;
}

pub(crate) mod m_2025_10_03 {
    mod settings;

    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2025_10_16 {
    mod settings;

    pub(crate) use settings::restore_code_actions_on_format;
}

pub(crate) mod m_2025_10_17 {
    mod settings;

    pub(crate) use settings::make_file_finder_include_ignored_an_enum;
}

pub(crate) mod m_2025_10_21 {
    mod settings;

    pub(crate) use settings::make_relative_line_numbers_an_enum;
}

pub(crate) mod m_2025_11_12 {
    mod settings;

    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2025_11_20 {
    mod settings;

    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2025_11_25 {
    mod settings;

    pub(crate) use settings::remove_context_server_source;
}

pub(crate) mod m_2025_12_01 {
    mod settings;

    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2025_12_08 {
    mod keymap;

    pub(crate) use keymap::KEYMAP_PATTERNS;
}

pub(crate) mod m_2025_12_15 {
    mod settings;

    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2025_01_27 {
    mod settings;

    pub(crate) use settings::make_auto_indent_an_enum;
}

pub(crate) mod m_2026_02_02 {
    mod settings;

    pub(crate) use settings::move_edit_prediction_provider_to_edit_predictions;
}

pub(crate) mod m_2026_02_03 {
    mod settings;

    pub(crate) use settings::migrate_experimental_sweep_mercury;
}

pub(crate) mod m_2026_02_04 {
    mod settings;

    pub(crate) use settings::migrate_tool_permission_defaults;
}

pub(crate) mod m_2026_02_25 {
    mod settings;

    pub(crate) use settings::migrate_builtin_agent_servers_to_registry;
}

pub(crate) mod m_2026_03_16 {
    mod settings;

    pub(crate) use settings::SETTINGS_PATTERNS;
}

pub(crate) mod m_2026_03_23 {
    mod keymap;

    pub(crate) use keymap::KEYMAP_PATTERNS;
}

pub(crate) mod m_2026_03_30 {
    mod settings;

    pub(crate) use settings::make_play_sound_when_agent_done_an_enum;
}

pub(crate) mod m_2026_04_01 {
    mod settings;

    pub(crate) use settings::restructure_profiles_with_settings_key;
}

pub(crate) mod m_2026_04_10 {
    mod settings;

    pub(crate) use settings::rename_web_search_to_search_web;
}

pub(crate) mod m_2026_04_17 {
    mod settings;

    pub(crate) use settings::promote_show_branch_icon_true_to_show_branch_status_icon;
}

pub(crate) mod m_2026_05_04 {
    mod settings;

    pub(crate) use settings::SETTINGS_PATTERNS;
}
