//! Configuration file finder with proper precedence hierarchy
//!
//! Provides utilities to find configuration files following this strict precedence:
//!
//! 1. **Explicit config path** - Via `--config` CLI argument or API parameter
//! 2. **Global config** - `~/.syntaxis/config.toml` or `~/.vapora/config.toml` (vapora takes precedence)
//! 3. **Local project config** - `./.vapora/config.toml` (always takes precedence over `.syntaxis/`)
//!    or `./.syntaxis/config.toml`
//!
//! # Configuration Discovery
//!
//! ```ignore
//! use config_finder::find_config_path;
//!
//! // Find config with proper precedence
//! let config_path = find_config_path("config.toml")?;
//! println!("Using config: {}", config_path.display());
//! ```

use std::path::{Path, PathBuf};

/// Finds configuration file with proper precedence hierarchy
///
/// # Search Order (in priority)
///
/// 1. Explicit config path (passed via argument)
/// 2. Global vapora config (`~/.vapora/{filename}`)
/// 3. Global syntaxis config (`~/.syntaxis/{filename}`)
/// 4. Local vapora config (`./.vapora/{filename}` - always takes precedence)
/// 5. Local syntaxis config (`./.syntaxis/{filename}`)
///
/// # Arguments
///
/// * `filename` - The config filename to search for (e.g., "config.toml")
/// * `explicit_config_path` - Optional explicit config path (highest priority)
///
/// # Returns
///
/// `Some(PathBuf)` pointing to the first existing config, or `None` if not found.
pub fn find_config_path(filename: &str, explicit_config_path: Option<&Path>) -> Option<PathBuf> {
    // 1. Check explicit config path (highest priority)
    if let Some(explicit_path) = explicit_config_path {
        if explicit_path.exists() {
            return Some(explicit_path.to_path_buf());
        }
    }

    // 2. Check global vapora config (takes precedence over syntaxis)
    if let Some(home) = dirs::home_dir() {
        let global_vapora = home.join(".vapora").join(filename);
        if global_vapora.exists() {
            return Some(global_vapora);
        }
    }

    // 3. Check global syntaxis config
    if let Some(home) = dirs::home_dir() {
        let global_syntaxis = home.join(".syntaxis").join(filename);
        if global_syntaxis.exists() {
            return Some(global_syntaxis);
        }
    }

    // 4. Check local vapora config (takes precedence over syntaxis)
    let local_vapora = PathBuf::from(".vapora").join(filename);
    if local_vapora.exists() {
        return Some(local_vapora);
    }

    // 5. Check local syntaxis config
    let local_syntaxis = PathBuf::from(".syntaxis").join(filename);
    if local_syntaxis.exists() {
        return Some(local_syntaxis);
    }

    None
}

/// Finds configuration file with default fallback
///
/// Like `find_config_path`, but returns the provided default if no config exists.
///
/// # Arguments
///
/// * `filename` - The config filename to search for
/// * `explicit_config_path` - Optional explicit config path
/// * `default` - Default path to return if not found
///
/// # Returns
///
/// First existing config path, or `default` if none found.
pub fn find_config_path_or<P: Into<PathBuf>>(
    filename: &str,
    explicit_config_path: Option<&Path>,
    default: P,
) -> PathBuf {
    find_config_path(filename, explicit_config_path).unwrap_or_else(|| default.into())
}

/// Gets the standard global syntaxis config directory
///
/// # Returns
///
/// `~/.syntaxis/` directory path
pub fn get_global_syntaxis_config_dir() -> Option<PathBuf> {
    dirs::home_dir().map(|home| home.join(".syntaxis"))
}

/// Gets the standard global vapora config directory
///
/// # Returns
///
/// `~/.vapora/` directory path
pub fn get_global_vapora_config_dir() -> Option<PathBuf> {
    dirs::home_dir().map(|home| home.join(".vapora"))
}

/// Gets the local syntaxis config directory
///
/// # Returns
///
/// `./.syntaxis/` directory path
pub fn get_local_syntaxis_config_dir() -> PathBuf {
    PathBuf::from(".syntaxis")
}

/// Gets the local vapora config directory
///
/// # Returns
///
/// `./.vapora/` directory path
pub fn get_local_vapora_config_dir() -> PathBuf {
    PathBuf::from(".vapora")
}

/// Finds config file with warnings for conflicting configurations
///
/// Like `find_config_path`, but emits a warning if multiple config locations are found.
/// Uses .vapora as precedence over .syntaxis at both global and local levels.
///
/// # Arguments
///
/// * `filename` - The config filename to search for
/// * `explicit_config_path` - Optional explicit config path
///
/// # Returns
///
/// First existing config path using precedence order, or `None` if not found.
pub fn find_config_path_warn_conflicts(
    filename: &str,
    explicit_config_path: Option<&Path>,
) -> Option<PathBuf> {
    if let Some(explicit_path) = explicit_config_path {
        if explicit_path.exists() {
            return Some(explicit_path.to_path_buf());
        }
    }

    let mut found_configs = vec![];

    // Global vapora (highest priority among globals)
    if let Some(home) = dirs::home_dir() {
        let global_vapora = home.join(".vapora").join(filename);
        if global_vapora.exists() {
            found_configs.push(("global .vapora".to_string(), global_vapora.clone()));
        }
    }

    // Global syntaxis
    if let Some(home) = dirs::home_dir() {
        let global_syntaxis = home.join(".syntaxis").join(filename);
        if global_syntaxis.exists() {
            found_configs.push(("global .syntaxis".to_string(), global_syntaxis.clone()));
        }
    }

    // Local vapora (takes precedence over local syntaxis)
    let local_vapora = PathBuf::from(".vapora").join(filename);
    if local_vapora.exists() {
        found_configs.push(("local .vapora".to_string(), local_vapora.clone()));
    }

    // Local syntaxis
    let local_syntaxis = PathBuf::from(".syntaxis").join(filename);
    if local_syntaxis.exists() {
        found_configs.push(("local .syntaxis".to_string(), local_syntaxis.clone()));
    }

    match found_configs.len() {
        0 => None,
        1 => Some(found_configs.into_iter().next().unwrap().1),
        _ => {
            // Multiple configs found - warn and use the highest priority one
            eprintln!(
                "⚠️  WARNING: Configuration {} found in multiple locations:",
                filename
            );
            for (location, _) in &found_configs {
                eprintln!("    • {}", location);
            }
            eprintln!("    Using: {} (highest precedence)", found_configs[0].0);
            eprintln!("    Please consolidate to a single location per precedence order");
            Some(found_configs.into_iter().next().unwrap().1)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs;
    use tempfile::TempDir;

    #[test]
    fn test_find_config_explicit_path() {
        let temp = TempDir::new().unwrap();
        let temp_path = temp.path().to_path_buf();
        let explicit_file = temp_path.join("explicit.toml");
        fs::write(&explicit_file, "explicit").unwrap();

        let result = find_config_path("config.toml", Some(&explicit_file));
        assert_eq!(
            result,
            Some(explicit_file),
            "Should prefer explicit config path"
        );
    }

    #[test]
    fn test_find_config_local_vapora_precedence() {
        let temp = TempDir::new().unwrap();
        let temp_path = temp.path().to_path_buf();

        // Create both local vapora and syntaxis
        let local_vapora = temp_path.join(".vapora");
        let local_syntaxis = temp_path.join(".syntaxis");
        fs::create_dir(&local_vapora).unwrap();
        fs::create_dir(&local_syntaxis).unwrap();

        fs::write(local_vapora.join("test.toml"), "vapora").unwrap();
        fs::write(local_syntaxis.join("test.toml"), "syntaxis").unwrap();

        let original = std::env::current_dir().unwrap();
        std::env::set_current_dir(&temp_path).unwrap();

        let result = find_config_path("test.toml", None);

        std::env::set_current_dir(&original).unwrap();

        assert_eq!(
            result,
            Some(PathBuf::from(".vapora/test.toml")),
            "Should prefer .vapora/ over .syntaxis/"
        );
    }

    #[test]
    fn test_find_config_not_found() {
        let temp = TempDir::new().unwrap();
        let original = std::env::current_dir().unwrap();
        std::env::set_current_dir(temp.path()).unwrap();

        let result = find_config_path("nonexistent.toml", None);
        assert_eq!(result, None, "Should return None when no config exists");

        std::env::set_current_dir(original).unwrap();
    }

    #[test]
    fn test_find_config_local_syntaxis() {
        let temp = TempDir::new().unwrap();
        let temp_path = temp.path().to_path_buf();

        // Create only local syntaxis
        let local_syntaxis = temp_path.join(".syntaxis");
        fs::create_dir(&local_syntaxis).unwrap();
        fs::write(local_syntaxis.join("test.toml"), "syntaxis").unwrap();

        let original = std::env::current_dir().unwrap();
        std::env::set_current_dir(&temp_path).unwrap();

        let result = find_config_path("test.toml", None);

        std::env::set_current_dir(&original).unwrap();

        assert_eq!(
            result,
            Some(PathBuf::from(".syntaxis/test.toml")),
            "Should find .syntaxis/ when .vapora/ doesn't exist"
        );
    }
}
