//! Platform path resolution for Zed editor directories.
//!
//! Resolves configuration and data directories for each Zed release channel
//! across Linux, macOS, and Windows.

use std::path::PathBuf;

/// Zed release channel. Each channel has independent config and data directories.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ZedChannel {
    Stable,
    Preview,
    Nightly,
    Dev,
}

impl ZedChannel {
    /// All known release channels.
    pub fn all() -> &'static [ZedChannel] {
        &[
            ZedChannel::Stable,
            ZedChannel::Preview,
            ZedChannel::Nightly,
            ZedChannel::Dev,
        ]
    }

    /// Socket/identifier name used in IPC paths.
    pub fn socket_name(&self) -> &'static str {
        match self {
            ZedChannel::Stable => "stable",
            ZedChannel::Preview => "preview",
            ZedChannel::Nightly => "nightly",
            ZedChannel::Dev => "dev",
        }
    }

    /// Display name for the channel.
    pub fn display_name(&self) -> &'static str {
        match self {
            ZedChannel::Stable => "Stable",
            ZedChannel::Preview => "Preview",
            ZedChannel::Nightly => "Nightly",
            ZedChannel::Dev => "Dev",
        }
    }
}

impl std::fmt::Display for ZedChannel {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(self.display_name())
    }
}

/// Resolve the Zed configuration directory for this platform.
///
/// | Platform | Path |
/// |----------|------|
/// | Linux    | `$XDG_CONFIG_HOME/zed` (default: `~/.config/zed`) |
/// | macOS    | `~/.config/zed` |
/// | Windows  | `%APPDATA%\Zed` |
pub fn zed_config_dir() -> Option<PathBuf> {
    #[cfg(target_os = "macos")]
    {
        dirs::home_dir().map(|d| d.join(".config").join("zed"))
    }
    #[cfg(target_os = "windows")]
    {
        dirs::config_dir().map(|d| d.join("Zed"))
    }
    #[cfg(not(any(target_os = "macos", target_os = "windows")))]
    {
        dirs::config_dir().map(|d| d.join("zed"))
    }
}

/// Resolve the Zed data directory for this platform.
///
/// | Platform | Path |
/// |----------|------|
/// | Linux    | `$XDG_DATA_HOME/zed` (default: `~/.local/share/zed`) |
/// | macOS    | `~/Library/Application Support/Zed` |
/// | Windows  | `%LOCALAPPDATA%\Zed` |
pub fn zed_data_dir() -> Option<PathBuf> {
    #[cfg(target_os = "windows")]
    {
        dirs::data_local_dir().map(|d| d.join("Zed"))
    }
    #[cfg(not(target_os = "windows"))]
    {
        dirs::data_dir().map(|d| d.join("zed"))
    }
}

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

    #[test]
    fn test_all_channels() {
        let channels = ZedChannel::all();
        assert_eq!(channels.len(), 4);
        assert!(channels.contains(&ZedChannel::Stable));
        assert!(channels.contains(&ZedChannel::Preview));
        assert!(channels.contains(&ZedChannel::Nightly));
        assert!(channels.contains(&ZedChannel::Dev));
    }

    #[test]
    fn test_socket_names() {
        assert_eq!(ZedChannel::Stable.socket_name(), "stable");
        assert_eq!(ZedChannel::Preview.socket_name(), "preview");
        assert_eq!(ZedChannel::Nightly.socket_name(), "nightly");
        assert_eq!(ZedChannel::Dev.socket_name(), "dev");
    }

    #[test]
    fn test_display_names() {
        assert_eq!(ZedChannel::Stable.to_string(), "Stable");
        assert_eq!(ZedChannel::Dev.to_string(), "Dev");
    }

    #[test]
    fn test_config_dir_is_some() {
        // On any supported platform, this should resolve.
        let dir = zed_config_dir();
        assert!(dir.is_some(), "zed_config_dir should resolve on this platform");
        let path = dir.unwrap();
        let path_str = path.to_string_lossy();
        assert!(
            path_str.contains("zed") || path_str.contains("Zed"),
            "config dir should contain 'zed': {path_str}"
        );
    }

    #[test]
    fn test_data_dir_is_some() {
        let dir = zed_data_dir();
        assert!(dir.is_some(), "zed_data_dir should resolve on this platform");
        let path = dir.unwrap();
        let path_str = path.to_string_lossy();
        assert!(
            path_str.contains("zed") || path_str.contains("Zed"),
            "data dir should contain 'zed': {path_str}"
        );
    }
}
