//! Tauri commands for reading and writing configuration.

use std::path::Path;

use crate::config::{GlobalConfig, ProjectConfig, SessionConfig};
use crate::state::AppState;
use tauri::State;

/// Load the global config from `~/.config/brittle/config.toml`.
/// Returns the default config if the file does not yet exist.
#[tauri::command]
pub fn load_global_config() -> Result<GlobalConfig, String> {
    GlobalConfig::load().map_err(|e| e.to_string())
}

/// Persist the global config to `~/.config/brittle/config.toml`.
#[tauri::command]
pub fn save_global_config(config: GlobalConfig) -> Result<(), String> {
    config.save().map_err(|e| e.to_string())
}

/// Load the per-project config from `{repo_path}/.brittle/config.toml`.
/// Returns the default config if the file does not yet exist.
#[tauri::command]
pub fn load_project_config(repo_path: String) -> Result<ProjectConfig, String> {
    ProjectConfig::load(Path::new(&repo_path)).map_err(|e| e.to_string())
}

/// Persist the per-project config to `{repo_path}/.brittle/config.toml`.
#[tauri::command]
pub fn save_project_config(repo_path: String, config: ProjectConfig) -> Result<(), String> {
    config
        .save(Path::new(&repo_path))
        .map_err(|e| e.to_string())
}

/// Return the current theme name (`"dark"` or `"light"`) from the global config.
#[tauri::command]
pub fn get_theme() -> Result<String, String> {
    use crate::config::Theme;
    GlobalConfig::load()
        .map(|c| match c.appearance.theme {
            Theme::Dark => "dark".to_string(),
            Theme::Light => "light".to_string(),
        })
        .map_err(|e| e.to_string())
}

/// Persist a new theme choice to the global config.
///
/// `theme` must be `"dark"` or `"light"`.
#[tauri::command]
pub fn set_theme(theme: String) -> Result<(), String> {
    use crate::config::Theme;
    let parsed = match theme.as_str() {
        "dark" => Theme::Dark,
        "light" => Theme::Light,
        other => return Err(format!("unknown theme '{other}'")),
    };
    let mut config = GlobalConfig::load().map_err(|e| e.to_string())?;
    config.appearance.theme = parsed;
    config.save().map_err(|e| e.to_string())
}

/// Return the last-opened project path, or `None` if none was recorded or
/// the directory no longer exists on disk.
#[tauri::command]
pub fn get_last_project() -> Result<Option<String>, String> {
    let cfg = GlobalConfig::load().map_err(|e| e.to_string())?;
    Ok(cfg
        .projects
        .last_opened
        .filter(|p| p.exists())
        .map(|p| p.to_string_lossy().into_owned()))
}

/// Return the user's layout configuration from the global config.
#[tauri::command]
pub fn get_layout_config() -> Result<crate::config::LayoutConfig, String> {
    GlobalConfig::load()
        .map(|c| c.layout)
        .map_err(|e| e.to_string())
}

/// Return the session state for the currently open project.
///
/// Returns `Default` if no session has been saved yet.
/// Errors if no repository is open.
#[tauri::command]
pub fn get_session(state: State<AppState>) -> Result<SessionConfig, String> {
    let guard = state.brittle.lock().map_err(|_| "lock poisoned".to_string())?;
    let brittle = guard.as_ref().ok_or_else(|| "no repository open".to_string())?;
    ProjectConfig::load(brittle.repository_root())
        .map(|c| c.session)
        .map_err(|e| e.to_string())
}

/// Persist the session state for the currently open project.
///
/// Errors if no repository is open.
#[tauri::command]
pub fn save_session(state: State<AppState>, session: SessionConfig) -> Result<(), String> {
    let guard = state.brittle.lock().map_err(|_| "lock poisoned".to_string())?;
    let brittle = guard.as_ref().ok_or_else(|| "no repository open".to_string())?;
    let root = brittle.repository_root();
    let mut config = ProjectConfig::load(root).map_err(|e| e.to_string())?;
    config.session = session;
    config.save(root).map_err(|e| e.to_string())
}

/// Return the user's keybinding overrides from the global config.
///
/// Map keys are action names in snake_case (e.g. `"tab_next"`); values are
/// key-sequence strings (e.g. `"<C-Right>"`).  Returns an empty map if no
/// config file exists or the `[keybindings]` section is absent.
#[tauri::command]
pub fn get_keybindings() -> Result<std::collections::HashMap<String, String>, String> {
    GlobalConfig::load()
        .map(|c| c.keybindings.0)
        .map_err(|e| e.to_string())
}
