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

/// Resolves configuration file paths with CLI flags priority
#[derive(Debug, Clone, Default)]
pub struct ConfigResolver {
    cli_config: Option<PathBuf>,
    cli_config_dir: Option<PathBuf>,
    cli_mode: Option<String>,
}

impl ConfigResolver {
    /// Create a new ConfigResolver with no CLI overrides
    pub fn new() -> Self {
        Self {
            cli_config: None,
            cli_config_dir: None,
            cli_mode: None,
        }
    }

    /// Set explicit config file path (highest priority)
    pub fn with_cli_config(mut self, path: Option<PathBuf>) -> Self {
        self.cli_config = path;
        self
    }

    /// Set config directory (searches for {service}.{ncl|toml|json})
    pub fn with_cli_config_dir(mut self, dir: Option<PathBuf>) -> Self {
        self.cli_config_dir = dir;
        self
    }

    /// Set deployment mode for config resolution
    pub fn with_cli_mode(mut self, mode: Option<String>) -> Self {
        self.cli_mode = mode;
        self
    }

    /// Resolve config file path with priority order:
    /// 1. CLI flag: -config <path> (explicit file)
    /// 2. CLI flag: --config-dir <dir> --mode <mode> (directory + mode)
    /// 3. CLI flag: --config-dir <dir> (directory, default naming)
    /// 4. CLI flag: --mode <mode> (mode in default path)
    /// 5. ENV var: {SERVICE}_CONFIG (explicit file)
    /// 6. ENV var: PROVISIONING_CONFIG_DIR (directory, default naming)
    /// 7. ENV var: {SERVICE}_MODE (mode in default path)
    /// 8. None (fallback to defaults)
    pub fn resolve(&self, service_name: &str) -> Option<PathBuf> {
        // Priority 1: CLI flag explicit path
        if let Some(ref path) = self.cli_config {
            tracing::debug!("Using CLI-provided config file: {:?}", path);
            return Some(path.clone());
        }

        // Priority 2: CLI config-dir + mode
        if let Some(ref dir) = self.cli_config_dir {
            if let Some(ref mode) = self.cli_mode {
                if let Some(config) = find_config_in_dir_with_mode(dir, service_name, mode) {
                    tracing::debug!(
                        "Using config file from CLI config-dir with mode: {:?}",
                        config
                    );
                    return Some(config);
                }
            }
        }

        // Priority 3: CLI config-dir only
        if let Some(ref dir) = self.cli_config_dir {
            if let Some(config) = find_config_in_dir(dir, service_name) {
                tracing::debug!("Using config file from CLI config-dir: {:?}", config);
                return Some(config);
            }
        }

        // Priority 4: CLI mode only (searches in default path)
        if let Some(ref mode) = self.cli_mode {
            if let Some(config) = super::hierarchy::find_config_file(service_name, mode) {
                tracing::debug!("Using config file with CLI mode: {:?}", config);
                return Some(config);
            }
        }

        // Priority 5-7: Fall back to environment variable resolution
        super::hierarchy::resolve_config_path(service_name)
    }
}

/// Search for config file in directory with specific mode
/// Searches in order: {service}.{mode}.ncl, {service}.{mode}.toml,
/// {service}.{mode}.json
pub fn find_config_in_dir_with_mode(dir: &Path, service_name: &str, mode: &str) -> Option<PathBuf> {
    for ext in &["ncl", "toml", "json"] {
        let path = dir.join(format!("{}.{}.{}", service_name, mode, ext));
        if path.exists() {
            tracing::trace!("Found config with mode: {:?}", path);
            return Some(path);
        }
    }
    None
}

/// Search for config file in directory with default naming
/// Searches in order: {service}.ncl, {service}.toml, {service}.json
pub fn find_config_in_dir(dir: &Path, service_name: &str) -> Option<PathBuf> {
    for ext in &["ncl", "toml", "json"] {
        let path = dir.join(format!("{}.{}", service_name, ext));
        if path.exists() {
            tracing::trace!("Found config in dir: {:?}", path);
            return Some(path);
        }
    }
    None
}

#[cfg(test)]
mod tests {
    use std::fs;

    use tempfile::TempDir;

    use super::*;

    #[test]
    fn test_cli_config_highest_priority() {
        let resolver =
            ConfigResolver::new().with_cli_config(Some(PathBuf::from("/explicit/path.toml")));

        let resolved = resolver.resolve("orchestrator");
        assert_eq!(resolved, Some(PathBuf::from("/explicit/path.toml")));
    }

    #[test]
    fn test_config_dir_searches_extensions_in_order() {
        let temp_dir = TempDir::new().unwrap();
        let ncl_path = temp_dir.path().join("orchestrator.ncl");
        let toml_path = temp_dir.path().join("orchestrator.toml");

        // Create both files
        fs::write(&ncl_path, "{}").unwrap();
        fs::write(&toml_path, "[orchestrator]").unwrap();

        let resolver =
            ConfigResolver::new().with_cli_config_dir(Some(temp_dir.path().to_path_buf()));

        let resolved = resolver.resolve("orchestrator").unwrap();
        // Should prefer .ncl over .toml
        assert_eq!(resolved, ncl_path);
    }

    #[test]
    fn test_config_dir_with_mode() {
        let temp_dir = TempDir::new().unwrap();
        let enterprise_path = temp_dir.path().join("orchestrator.enterprise.toml");
        fs::write(&enterprise_path, "[orchestrator]").unwrap();

        let resolver = ConfigResolver::new()
            .with_cli_config_dir(Some(temp_dir.path().to_path_buf()))
            .with_cli_mode(Some("enterprise".to_string()));

        let resolved = resolver.resolve("orchestrator").unwrap();
        assert_eq!(resolved, enterprise_path);
    }

    #[test]
    fn test_find_config_in_dir_prefers_ncl() {
        let temp_dir = TempDir::new().unwrap();
        let ncl_path = temp_dir.path().join("test-service.ncl");
        let toml_path = temp_dir.path().join("test-service.toml");
        let json_path = temp_dir.path().join("test-service.json");

        fs::write(&ncl_path, "{}").unwrap();
        fs::write(&toml_path, "[test]").unwrap();
        fs::write(&json_path, "{}").unwrap();

        let result = find_config_in_dir(temp_dir.path(), "test-service").unwrap();
        assert_eq!(result, ncl_path);
    }

    #[test]
    fn test_find_config_in_dir_with_mode_json_fallback() {
        let temp_dir = TempDir::new().unwrap();
        let json_path = temp_dir.path().join("test-service.solo.json");
        fs::write(&json_path, "{}").unwrap();

        let result = find_config_in_dir_with_mode(temp_dir.path(), "test-service", "solo").unwrap();
        assert_eq!(result, json_path);
    }

    #[test]
    fn test_no_config_found_returns_none() {
        let temp_dir = TempDir::new().unwrap();

        let resolver =
            ConfigResolver::new().with_cli_config_dir(Some(temp_dir.path().to_path_buf()));

        let resolved = resolver.resolve("nonexistent-service");
        assert!(resolved.is_none());
    }

    #[test]
    fn test_cli_config_dir_overrides_env_var() {
        let temp_dir = TempDir::new().unwrap();
        let config_path = temp_dir.path().join("orchestrator.toml");
        fs::write(&config_path, "[orchestrator]").unwrap();

        // This test just verifies the resolver logic works correctly
        // (actual env var override would need temp_env crate)
        let resolver =
            ConfigResolver::new().with_cli_config_dir(Some(temp_dir.path().to_path_buf()));

        let resolved = resolver.resolve("orchestrator").unwrap();
        assert_eq!(resolved, config_path);
    }
}
