macro_rules! adapter {
    ($name:ident, $group:expr, $type:path $(, $($attr:tt)*)?) => {
        paste::paste! {
            $($attr)*
            pub(crate) static [<$name:upper _KEY>]: &str = concat!($group, ".", stringify!($name));

            $($attr)*
            pub(crate) fn $name(settings: &$crate::settings::Settings) -> &$type {
                settings.get::<$type>([<$name:upper _KEY>])
            }
        }
    };
}

pub(crate) mod editor {
    #![allow(dead_code)]

    pub(crate) const EDITOR_KEY: &str = "editor";

    macro_rules! editor_adapter {
        ($name:ident, $type:path) => {
            adapter!($name, "editor", $type);
        };
    }

    editor_adapter!(show_line_numbers, bool);
    editor_adapter!(word_wrap, bool);
    editor_adapter!(use_space, bool);
    editor_adapter!(indentation_width, i64);
    editor_adapter!(trim_on_save, bool);

    editor_adapter!(high_contrast, bool);
    editor_adapter!(cursor_style, String);
    editor_adapter!(cursor_blink, bool);
    editor_adapter!(reduced_motion, bool);
    editor_adapter!(dim_non_focused, bool);
    editor_adapter!(line_spacing, f64);
}

pub(crate) mod theme {
    #![allow(dead_code)]

    adapter!(name, "theme", String);
}

pub(crate) mod lsp {
    #![allow(dead_code)]

    pub(crate) const LSP_KEY: &str = "lsp";

    macro_rules! lsp_adapter {
        ($name:ident, $type:path) => {
            adapter!($name, "lsp", $type);
        };
    }

    lsp_adapter!(enable, bool);
    lsp_adapter!(enable_auto_completion, bool);
    lsp_adapter!(completion_delay_ms, i64);
    lsp_adapter!(server_command, String);
    lsp_adapter!(server_args, Vec<String>);
}

pub(crate) mod language {
    /// Generate a per-language accessor that checks `languages.<lang>.<group>.<name>`
    /// first, then falls back to the global `<group>.<name>` setting.
    macro_rules! lang_adapter {
        ($name:ident, $group:expr, $type:path) => {
            paste::paste! {
                pub(crate) fn $name<'a>(settings: &'a $crate::settings::Settings, lang: &str) -> &'a $type {
                    let key = format!(concat!("languages.{}.", $group, ".", stringify!($name)), lang);
                    settings
                        .get_optional::<$type>(&key)
                        .unwrap_or_else(|| settings.get::<$type>(concat!($group, ".", stringify!($name))))
                }
            }
        };
    }

    pub(crate) mod lsp {
        #![allow(dead_code)]

        macro_rules! lsp_lang_adapter {
            ($name:ident, $type:path) => {
                lang_adapter!($name, "lsp", $type);
            };
        }

        lsp_lang_adapter!(enable, bool);
        lsp_lang_adapter!(enable_auto_completion, bool);
        lsp_lang_adapter!(completion_delay_ms, i64);
        lsp_lang_adapter!(server_command, String);
        lsp_lang_adapter!(server_args, Vec<String>);
    }
}

pub(crate) mod terminal {
    #![allow(dead_code)]

    pub(crate) const TERMINAL_KEY: &str = "terminal";

    macro_rules! terminal_adapter {
        ($name:ident, $type:path) => {
            adapter!($name, "terminal", $type);
        };
    }

    terminal_adapter!(scrollback_lines, i64);
    terminal_adapter!(scroll_size, i64);
    terminal_adapter!(shell, String);
}

/// Per-language LSP configuration. All fields fall back to the global `lsp.*`
/// settings when not specified per-language.
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub(crate) struct LspSettings {
    pub enable: bool,
    pub server_command: String,
    pub server_args: Vec<String>,
    pub enable_auto_completion: bool,
    pub completion_delay_ms: i64,
}

/// Per-language configuration resolved from a `languages.<lang_id>.*` section.
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub(crate) struct LanguageSettings {
    /// LSP configuration for this language. `None` when the YAML contained no
    /// `languages.<lang_id>.lsp` sub-section at all.
    pub lsp: Option<LspSettings>,
}


