use crate::error::{CjvError, Result};
use std::path::{Path, PathBuf};

/// Platform identifiers (GOOS-GOARCH style)
pub fn current_platform() -> Result<String> {
    let os = std::env::consts::OS;
    let arch = std::env::consts::ARCH;

    let goos = match os {
        "linux" => "linux",
        "macos" | "darwin" => "darwin",
        "windows" => "windows",
        _ => return Err(CjvError::UnsupportedPlatform(os.to_string())),
    };

    let goarch = match arch {
        "x86_64" => "amd64",
        "aarch64" | "arm64" => "arm64",
        _ => return Err(CjvError::UnsupportedPlatform(arch.to_string())),
    };

    Ok(format!("{}-{}", goos, goarch))
}

/// Get the CJV_HOME directory
pub fn cjv_home() -> PathBuf {
    if let Ok(val) = std::env::var("CJV_HOME") {
        return PathBuf::from(val);
    }

    // Check settings.toml for home override
    let settings_path = settings_file();
    if settings_path.exists() {
        if let Ok(content) = std::fs::read_to_string(&settings_path) {
            if let Ok(settings) = content.parse::<toml::Value>() {
                if let Some(home) = settings.get("home").and_then(|v| v.as_str()) {
                    return PathBuf::from(home);
                }
            }
        }
    }

    dirs::home_dir()
        .map(|h| h.join(".cjv"))
        .unwrap_or_else(|| PathBuf::from(".cjv"))
}

/// Get the settings.toml path.
pub fn settings_file() -> PathBuf {
    if let Ok(val) = std::env::var("CJV_HOME") {
        return PathBuf::from(val).join("settings.toml");
    }

    dirs::home_dir()
        .map(|h| h.join(".cjv").join("settings.toml"))
        .unwrap_or_else(|| PathBuf::from(".cjv/settings.toml"))
}

/// Ensure a directory exists
pub fn ensure_dir(path: &Path) -> Result<()> {
    std::fs::create_dir_all(path).map_err(|e| {
        CjvError::Other(format!(
            "Failed to create directory {}: {}",
            path.display(),
            e
        ))
    })
}

/// Symlink helper - creates a symlink (or junction on Windows)
pub fn create_symlink(target: &Path, link: &Path) -> Result<()> {
    #[cfg(unix)]
    {
        std::os::unix::fs::symlink(target, link)?;
    }
    #[cfg(windows)]
    {
        if target.is_dir() {
            std::os::windows::fs::symlink_dir(target, link)?;
        } else {
            std::os::windows::fs::symlink_file(target, link)?;
        }
    }
    Ok(())
}

/// Check if a string is a valid channel name
pub fn is_valid_channel(name: &str) -> bool {
    matches!(name, "lts" | "sts" | "nightly")
}

/// Check if a name is a reserved channel
pub fn is_reserved_name(name: &str) -> bool {
    is_valid_channel(name)
        || name.starts_with("lts-")
        || name.starts_with("sts-")
        || name.starts_with("nightly-")
}

/// True if `path` is a filesystem root or the user's home directory itself —
/// never a safe target for a cjv data directory (destructive ops would wipe it).
pub fn is_unsafe_cjv_home(path: &Path) -> bool {
    if path == Path::new("/") || path.as_os_str().is_empty() {
        return true;
    }
    #[cfg(windows)]
    {
        if path.components().count() <= 1 {
            return true;
        }
    }
    dirs::home_dir()
        .map(|h| h == path)
        .unwrap_or(false)
}

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

    #[test]
    fn detects_filesystem_root_as_unsafe_home() {
        assert!(is_unsafe_cjv_home(Path::new("/")));
        assert!(is_unsafe_cjv_home(Path::new("")));
    }

    #[test]
    fn detects_user_home_as_unsafe_home() {
        if let Some(home) = dirs::home_dir() {
            assert!(is_unsafe_cjv_home(&home));
        }
    }

    #[test]
    fn normal_cjv_home_is_safe() {
        let temp = tempfile::tempdir().unwrap();
        let home = temp.path().join(".cjv");
        assert!(!is_unsafe_cjv_home(&home));
    }
}