//! Default paths for shepherdd components
//!
//! Provides centralized path defaults that all crates can use.
//! Paths are user-writable by default (no root required):
//! - Socket: `$XDG_RUNTIME_DIR/shepherdd/shepherdd.sock` or `/tmp/shepherdd-$USER/shepherdd.sock`
//! - Data: `$XDG_DATA_HOME/shepherdd` or `~/.local/share/shepherdd`
//! - Logs: `$XDG_STATE_HOME/shepherdd` or `~/.local/state/shepherdd`

use std::path::PathBuf;

/// Environment variable for overriding the socket path
pub const SHEPHERD_SOCKET_ENV: &str = "SHEPHERD_SOCKET";

/// Environment variable for overriding the data directory
pub const SHEPHERD_DATA_DIR_ENV: &str = "SHEPHERD_DATA_DIR";

/// Socket filename within the socket directory
const SOCKET_FILENAME: &str = "shepherdd.sock";

/// Application subdirectory name
const APP_DIR: &str = "shepherdd";

/// Get the default socket path.
///
/// Order of precedence:
/// 1. `$SHEPHERD_SOCKET` environment variable (if set)
/// 2. `$XDG_RUNTIME_DIR/shepherdd/shepherdd.sock` (if XDG_RUNTIME_DIR is set)
/// 3. `/tmp/shepherdd-$USER/shepherdd.sock` (fallback)
pub fn default_socket_path() -> PathBuf {
    // Check environment override first
    if let Ok(path) = std::env::var(SHEPHERD_SOCKET_ENV) {
        return PathBuf::from(path);
    }

    socket_path_without_env()
}

/// Get the socket path without checking SHEPHERD_SOCKET env var.
/// Used for default values in configs where the env var is checked separately.
pub fn socket_path_without_env() -> PathBuf {
    // Try XDG_RUNTIME_DIR first (typically /run/user/<uid>)
    if let Ok(runtime_dir) = std::env::var("XDG_RUNTIME_DIR") {
        return PathBuf::from(runtime_dir)
            .join(APP_DIR)
            .join(SOCKET_FILENAME);
    }

    // Fallback to /tmp with username
    let username = std::env::var("USER").unwrap_or_else(|_| "unknown".to_string());
    PathBuf::from(format!("/tmp/{}-{}", APP_DIR, username)).join(SOCKET_FILENAME)
}

/// Get the default data directory.
///
/// Order of precedence:
/// 1. `$SHEPHERD_DATA_DIR` environment variable (if set)
/// 2. `$XDG_DATA_HOME/shepherdd` (if XDG_DATA_HOME is set)
/// 3. `~/.local/share/shepherdd` (fallback)
pub fn default_data_dir() -> PathBuf {
    // Check environment override first
    if let Ok(path) = std::env::var(SHEPHERD_DATA_DIR_ENV) {
        return PathBuf::from(path);
    }

    data_dir_without_env()
}

/// Get the data directory without checking SHEPHERD_DATA_DIR env var.
/// Used for default values in configs where the env var is checked separately.
pub fn data_dir_without_env() -> PathBuf {
    // Try XDG_DATA_HOME first
    if let Ok(data_home) = std::env::var("XDG_DATA_HOME") {
        return PathBuf::from(data_home).join(APP_DIR);
    }

    // Fallback to ~/.local/share/shepherdd
    if let Ok(home) = std::env::var("HOME") {
        return PathBuf::from(home)
            .join(".local")
            .join("share")
            .join(APP_DIR);
    }

    // Last resort
    PathBuf::from("/tmp").join(APP_DIR).join("data")
}

/// Get the default log directory.
///
/// Order of precedence:
/// 1. `$XDG_STATE_HOME/shepherdd` (if XDG_STATE_HOME is set)
/// 2. `~/.local/state/shepherdd` (fallback)
pub fn default_log_dir() -> PathBuf {
    // Try XDG_STATE_HOME first
    if let Ok(state_home) = std::env::var("XDG_STATE_HOME") {
        return PathBuf::from(state_home).join(APP_DIR);
    }

    // Fallback to ~/.local/state/shepherdd
    if let Ok(home) = std::env::var("HOME") {
        return PathBuf::from(home)
            .join(".local")
            .join("state")
            .join(APP_DIR);
    }

    // Last resort
    PathBuf::from("/tmp").join(APP_DIR).join("logs")
}

/// Get the parent directory of the socket (for creating it)
pub fn socket_dir() -> PathBuf {
    let socket_path = socket_path_without_env();
    socket_path
        .parent()
        .map(|p| p.to_path_buf())
        .unwrap_or_else(|| {
            // Should never happen with our paths, but just in case
            PathBuf::from("/tmp").join(APP_DIR)
        })
}

/// Configuration subdirectory name (uses "shepherd" not "shepherdd")
const CONFIG_APP_DIR: &str = "shepherd";

/// Configuration filename
const CONFIG_FILENAME: &str = "config.toml";

/// Get the default configuration file path.
///
/// Returns `$XDG_CONFIG_HOME/shepherd/config.toml` or `~/.config/shepherd/config.toml`
pub fn default_config_path() -> PathBuf {
    // Try XDG_CONFIG_HOME first
    if let Ok(config_home) = std::env::var("XDG_CONFIG_HOME") {
        return PathBuf::from(config_home)
            .join(CONFIG_APP_DIR)
            .join(CONFIG_FILENAME);
    }

    // Fallback to ~/.config/shepherd/config.toml
    if let Ok(home) = std::env::var("HOME") {
        return PathBuf::from(home)
            .join(".config")
            .join(CONFIG_APP_DIR)
            .join(CONFIG_FILENAME);
    }

    // Last resort (unlikely to be valid, but provides a fallback)
    PathBuf::from("/etc")
        .join(CONFIG_APP_DIR)
        .join(CONFIG_FILENAME)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn socket_path_contains_shepherdd() {
        // The socket path should always contain "shepherdd" regardless of environment
        let path = socket_path_without_env();
        assert!(path.to_string_lossy().contains("shepherdd"));
        assert!(path.to_string_lossy().contains(".sock"));
    }

    #[test]
    fn data_dir_contains_shepherdd() {
        let path = data_dir_without_env();
        assert!(path.to_string_lossy().contains("shepherdd"));
    }

    #[test]
    fn log_dir_contains_shepherdd() {
        let path = default_log_dir();
        assert!(path.to_string_lossy().contains("shepherdd"));
    }

    #[test]
    fn socket_dir_is_parent_of_socket_path() {
        let socket = socket_path_without_env();
        let dir = socket_dir();
        assert_eq!(socket.parent().unwrap(), dir);
    }

    #[test]
    fn config_path_contains_shepherd() {
        let path = default_config_path();
        assert!(path.to_string_lossy().contains("shepherd"));
        assert!(path.to_string_lossy().ends_with("config.toml"));
    }
}
