use std::path::PathBuf;

/// Represents a single discovered config file location
#[derive(Debug, Clone)]
pub struct ConfigLocation {
    pub level: ConfigLevel,
    pub path: PathBuf,
    pub exists: bool,
    pub writable: bool,
    pub description: String,
}

#[derive(Debug, Clone, PartialEq)]
pub enum ConfigLevel {
    System,
    User,
    Site,
    Env,
}

impl std::fmt::Display for ConfigLevel {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ConfigLevel::System => write!(f, "system"),
            ConfigLevel::User => write!(f, "user"),
            ConfigLevel::Site => write!(f, "site"),
            ConfigLevel::Env => write!(f, "env"),
        }
    }
}

/// Discover all pip config file paths for the current platform.
/// Order follows pip's own precedence: site < user < system < env
pub fn discover_all() -> Vec<ConfigLocation> {
    let mut locations = vec![];

    // --- SITE (venv / virtualenv) ---
    locations.extend(discover_site_locations());

    // --- USER ---
    locations.extend(discover_user_locations());

    // --- SYSTEM / GLOBAL ---
    locations.extend(discover_system_locations());

    // --- PIP_CONFIG_FILE env override ---
    if let Ok(env_path) = std::env::var("PIP_CONFIG_FILE") {
        let p = PathBuf::from(&env_path);
        locations.push(ConfigLocation {
            level: ConfigLevel::Env,
            exists: p.exists(),
            writable: is_writable(&p),
            description: "PIP_CONFIG_FILE environment variable".to_string(),
            path: p,
        });
    }

    // Fill exists/writable
    for loc in &mut locations {
        loc.exists = loc.path.exists();
        loc.writable = is_writable(&loc.path);
    }

    locations
}

fn discover_user_locations() -> Vec<ConfigLocation> {
    let mut locs = vec![];

    #[cfg(target_os = "windows")]
    {
        // %APPDATA%\pip\pip.ini
        if let Some(appdata) = dirs::config_dir() {
            locs.push(loc(
                ConfigLevel::User,
                appdata.join("pip").join("pip.ini"),
                "%APPDATA%\\pip\\pip.ini (Windows user config)",
            ));
        }
        // %HOMEPATH%\pip\pip.ini  (legacy)
        if let Some(home) = dirs::home_dir() {
            locs.push(loc(
                ConfigLevel::User,
                home.join("pip").join("pip.ini"),
                "%HOMEPATH%\\pip\\pip.ini (Windows legacy user config)",
            ));
        }
    }

    #[cfg(target_os = "macos")]
    {
        // $HOME/Library/Application Support/pip/pip.conf (XDG-style on macOS)
        if let Some(home) = dirs::home_dir() {
            locs.push(loc(
                ConfigLevel::User,
                home.join("Library")
                    .join("Application Support")
                    .join("pip")
                    .join("pip.conf"),
                "~/Library/Application Support/pip/pip.conf (macOS user config)",
            ));
        }
        // $HOME/.config/pip/pip.conf
        if let Some(config_dir) = dirs::config_dir() {
            locs.push(loc(
                ConfigLevel::User,
                config_dir.join("pip").join("pip.conf"),
                "~/.config/pip/pip.conf (XDG user config)",
            ));
        }
        // $HOME/.pip/pip.conf  (legacy)
        if let Some(home) = dirs::home_dir() {
            locs.push(loc(
                ConfigLevel::User,
                home.join(".pip").join("pip.conf"),
                "~/.pip/pip.conf (legacy user config)",
            ));
        }
    }

    #[cfg(target_os = "linux")]
    {
        // $XDG_CONFIG_HOME/pip/pip.conf  (default: ~/.config/pip/pip.conf)
        let xdg_config = std::env::var("XDG_CONFIG_HOME")
            .map(PathBuf::from)
            .unwrap_or_else(|_| dirs::home_dir().unwrap_or_default().join(".config"));
        locs.push(loc(
            ConfigLevel::User,
            xdg_config.join("pip").join("pip.conf"),
            "$XDG_CONFIG_HOME/pip/pip.conf (XDG user config)",
        ));
        // $HOME/.pip/pip.conf  (legacy)
        if let Some(home) = dirs::home_dir() {
            locs.push(loc(
                ConfigLevel::User,
                home.join(".pip").join("pip.conf"),
                "~/.pip/pip.conf (legacy user config)",
            ));
        }
    }

    // Generic fallback for any other Unix-like
    #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
    {
        if let Some(home) = dirs::home_dir() {
            locs.push(loc(
                ConfigLevel::User,
                home.join(".config").join("pip").join("pip.conf"),
                "~/.config/pip/pip.conf",
            ));
            locs.push(loc(
                ConfigLevel::User,
                home.join(".pip").join("pip.conf"),
                "~/.pip/pip.conf (legacy)",
            ));
        }
    }

    locs
}

fn discover_system_locations() -> Vec<ConfigLocation> {
    let mut locs = vec![];

    #[cfg(target_os = "windows")]
    {
        // %ProgramData%\pip\pip.ini
        if let Ok(program_data) = std::env::var("ProgramData") {
            locs.push(loc(
                ConfigLevel::System,
                PathBuf::from(&program_data).join("pip").join("pip.ini"),
                "%ProgramData%\\pip\\pip.ini (Windows system config)",
            ));
        }
        // C:\ProgramData\pip\pip.ini fallback
        locs.push(loc(
            ConfigLevel::System,
            PathBuf::from(r"C:\ProgramData\pip\pip.ini"),
            "C:\\ProgramData\\pip\\pip.ini (Windows system config fallback)",
        ));
    }

    #[cfg(target_os = "macos")]
    {
        locs.push(loc(
            ConfigLevel::System,
            PathBuf::from("/Library/Application Support/pip/pip.conf"),
            "/Library/Application Support/pip/pip.conf (macOS system config)",
        ));
        locs.push(loc(
            ConfigLevel::System,
            PathBuf::from("/etc/pip.conf"),
            "/etc/pip.conf (Unix system config)",
        ));
    }

    #[cfg(target_os = "linux")]
    {
        locs.push(loc(
            ConfigLevel::System,
            PathBuf::from("/etc/pip.conf"),
            "/etc/pip.conf (Linux system config)",
        ));
        locs.push(loc(
            ConfigLevel::System,
            PathBuf::from("/etc/xdg/pip/pip.conf"),
            "/etc/xdg/pip/pip.conf (XDG system config)",
        ));
    }

    #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
    {
        locs.push(loc(
            ConfigLevel::System,
            PathBuf::from("/etc/pip.conf"),
            "/etc/pip.conf (Unix system config)",
        ));
    }

    locs
}

fn discover_site_locations() -> Vec<ConfigLocation> {
    let mut locs = vec![];

    // Active virtualenv / venv
    if let Ok(venv) = std::env::var("VIRTUAL_ENV") {
        let base = PathBuf::from(&venv);
        #[cfg(target_os = "windows")]
        let site_path = base.join("pip.ini");
        #[cfg(not(target_os = "windows"))]
        let site_path = base.join("pip.conf");

        locs.push(loc(
            ConfigLevel::Site,
            site_path,
            "$VIRTUAL_ENV/pip.conf (active virtualenv site config)",
        ));
    }

    // Conda environment
    if let Ok(conda_prefix) = std::env::var("CONDA_PREFIX") {
        let base = PathBuf::from(&conda_prefix);
        #[cfg(target_os = "windows")]
        let site_path = base.join("pip.ini");
        #[cfg(not(target_os = "windows"))]
        let site_path = base.join("pip.conf");

        locs.push(loc(
            ConfigLevel::Site,
            site_path,
            "$CONDA_PREFIX/pip.conf (active conda env site config)",
        ));
    }

    locs
}

fn loc(level: ConfigLevel, path: PathBuf, description: &str) -> ConfigLocation {
    let exists = path.exists();
    let writable = is_writable(&path);
    ConfigLocation {
        level,
        path,
        exists,
        writable,
        description: description.to_string(),
    }
}

fn is_writable(path: &PathBuf) -> bool {
    if path.exists() {
        // Try to check write permission by opening in append mode
        std::fs::OpenOptions::new().append(true).open(path).is_ok()
    } else {
        // Check if parent directory is writable
        if let Some(parent) = path.parent() {
            if parent.as_os_str().is_empty() {
                return true;
            }
            if !parent.exists() {
                return true; // We can create it
            }
            std::fs::OpenOptions::new()
                .write(true)
                .create(true)
                .truncate(true)
                .open(parent.join(".pipconf_write_test"))
                .map(|_| {
                    let _ = std::fs::remove_file(parent.join(".pipconf_write_test"));
                    true
                })
                .unwrap_or(false)
        } else {
            false
        }
    }
}

/// Return the highest-priority existing config file (user-level by default)
pub fn active_user_config() -> Option<PathBuf> {
    // Check env override first
    if let Ok(env_path) = std::env::var("PIP_CONFIG_FILE") {
        let p = PathBuf::from(&env_path);
        if p.exists() {
            return Some(p);
        }
    }

    // Then user-level, first existing
    for loc in discover_user_locations() {
        if loc.path.exists() {
            return Some(loc.path);
        }
    }

    // Return the first user-level path (even if not existing) as the write target
    discover_user_locations().into_iter().next().map(|l| l.path)
}

/// Return the default write path (user-level, preferred)
pub fn default_write_path() -> PathBuf {
    active_user_config().unwrap_or_else(|| {
        #[cfg(target_os = "windows")]
        {
            dirs::config_dir()
                .unwrap_or_else(|| PathBuf::from("."))
                .join("pip")
                .join("pip.ini")
        }
        #[cfg(not(target_os = "windows"))]
        {
            dirs::home_dir()
                .unwrap_or_else(|| PathBuf::from("."))
                .join(".config")
                .join("pip")
                .join("pip.conf")
        }
    })
}
