use std::env;
use std::path::PathBuf;

use crate::error::{ConfigError, Result};

/// Team-specific configuration resolver
///
/// Configuration hierarchy (high to low priority):
/// 1. Explicit PROVISIONING_TEAM env var
/// 2. Current user's team (from HOME/.provisioning-team)
/// 3. Default team (if exists)
#[derive(Debug, Clone)]
pub struct TeamConfigResolver {
    current_team: Option<String>,
    team_config_dir: PathBuf,
}

impl TeamConfigResolver {
    /// Create a new team config resolver
    pub fn new(team_config_base_dir: PathBuf) -> Self {
        let current_team = Self::detect_current_team();

        Self {
            current_team,
            team_config_dir: team_config_base_dir,
        }
    }

    /// Detect current team from environment or user config
    fn detect_current_team() -> Option<String> {
        // Priority 1: Explicit env var
        if let Ok(team) = env::var("PROVISIONING_TEAM") {
            tracing::debug!("Using team from PROVISIONING_TEAM: {}", team);
            return Some(team);
        }

        // Priority 2: User's team file
        let Ok(home) = env::var("HOME") else {
            return None;
        };
        let team_file = PathBuf::from(home).join(".provisioning-team");
        if !team_file.exists() {
            return None;
        }
        let content = match std::fs::read_to_string(&team_file) {
            Ok(c) => c,
            Err(e) => {
                tracing::warn!("Failed to read ~/.provisioning-team: {}", e);
                return None;
            }
        };
        let team = content.trim().to_string();
        if team.is_empty() {
            return None;
        }
        tracing::debug!("Using team from ~/.provisioning-team: {}", team);
        Some(team)
    }

    /// Get the current team name
    pub fn current_team(&self) -> Option<&str> {
        self.current_team.as_deref()
    }

    /// Get the team config directory for a specific team
    pub fn team_dir(&self, team_name: &str) -> PathBuf {
        self.team_config_dir.join("teams").join(team_name)
    }

    /// Get the team config directory for current team
    pub fn current_team_dir(&self) -> Option<PathBuf> {
        self.current_team.as_ref().map(|team| self.team_dir(team))
    }

    /// Check if a team exists
    pub fn team_exists(&self, team_name: &str) -> bool {
        self.team_dir(team_name).exists()
    }

    /// List all available teams
    pub fn list_teams(&self) -> Result<Vec<String>> {
        let teams_dir = self.team_config_dir.join("teams");

        if !teams_dir.exists() {
            return Ok(Vec::new());
        }

        let mut teams = Vec::new();

        match std::fs::read_dir(&teams_dir) {
            Ok(entries) => {
                teams.extend(
                    entries
                        .flatten()
                        .filter(|e| e.path().is_dir())
                        .filter_map(|e| e.file_name().to_str().map(|s| s.to_string())),
                );
                teams.sort();
                Ok(teams)
            }
            Err(e) => Err(ConfigError::io_error(format!(
                "Failed to list teams: {}",
                e
            ))),
        }
    }

    /// Get team-specific config file for a service
    /// Returns the path if it exists
    pub fn get_service_config(&self, service_name: &str) -> Option<PathBuf> {
        let current_team = self.current_team.as_ref()?;
        let team_dir = self.team_dir(current_team);

        // Try {service-name}.ncl first
        let ncl_path = team_dir.join(format!("{}.ncl", service_name));
        if ncl_path.exists() {
            tracing::debug!("Found team config for {}: {:?}", service_name, ncl_path);
            return Some(ncl_path);
        }

        // Try {service-name}.toml
        let toml_path = team_dir.join(format!("{}.toml", service_name));
        if toml_path.exists() {
            tracing::debug!("Found team config for {}: {:?}", service_name, toml_path);
            return Some(toml_path);
        }

        None
    }

    /// Display team information
    pub fn display_info(&self) -> String {
        let mut info = String::new();
        info.push_str("👥 Team Configuration\n");
        info.push_str("─────────────────────\n");

        if let Some(team) = &self.current_team {
            info.push_str(&format!("Current Team: {}\n", team));
            info.push_str(&format!("Team Dir: {:?}\n", self.current_team_dir()));
        } else {
            info.push_str("Current Team: (none)\n");
            info.push_str("Using global defaults\n");
        }

        match self.list_teams() {
            Ok(teams) if !teams.is_empty() => {
                info.push_str(&format!("Available Teams: {}\n", teams.join(", ")));
            }
            _ => {
                info.push_str("Available Teams: (none)\n");
            }
        }

        info
    }
}

#[cfg(test)]
mod tests {
    use tempfile::TempDir;

    use super::*;

    #[test]
    fn test_team_resolver_new() {
        let temp_dir = TempDir::new().unwrap();
        let resolver = TeamConfigResolver::new(temp_dir.path().to_path_buf());
        assert_eq!(resolver.team_config_dir, temp_dir.path());
    }

    #[test]
    fn test_list_teams_empty() {
        let temp_dir = TempDir::new().unwrap();
        std::fs::create_dir(temp_dir.path().join("teams")).unwrap();

        let resolver = TeamConfigResolver::new(temp_dir.path().to_path_buf());
        let teams = resolver.list_teams().unwrap();
        assert!(teams.is_empty());
    }

    #[test]
    fn test_list_teams_with_content() {
        let temp_dir = TempDir::new().unwrap();
        let teams_dir = temp_dir.path().join("teams");
        std::fs::create_dir(&teams_dir).unwrap();

        // Create test teams
        std::fs::create_dir(teams_dir.join("team-a")).unwrap();
        std::fs::create_dir(teams_dir.join("team-b")).unwrap();

        let resolver = TeamConfigResolver::new(temp_dir.path().to_path_buf());
        let teams = resolver.list_teams().unwrap();

        assert_eq!(teams.len(), 2);
        assert!(teams.contains(&"team-a".to_string()));
        assert!(teams.contains(&"team-b".to_string()));
        println!("✓ Teams list: {:?}", teams);
    }

    #[test]
    fn test_team_exists() {
        let temp_dir = TempDir::new().unwrap();
        let teams_dir = temp_dir.path().join("teams");
        std::fs::create_dir(&teams_dir).unwrap();
        std::fs::create_dir(teams_dir.join("team-a")).unwrap();

        let resolver = TeamConfigResolver::new(temp_dir.path().to_path_buf());
        assert!(resolver.team_exists("team-a"));
        assert!(!resolver.team_exists("team-b"));
    }

    #[test]
    fn test_get_service_config_with_team() {
        let temp_dir = TempDir::new().unwrap();
        let teams_dir = temp_dir.path().join("teams").join("team-a");
        std::fs::create_dir_all(&teams_dir).unwrap();

        // Create service config
        std::fs::write(teams_dir.join("vault-service.ncl"), "{}").unwrap();

        let mut resolver = TeamConfigResolver::new(temp_dir.path().to_path_buf());
        resolver.current_team = Some("team-a".to_string());

        let config = resolver.get_service_config("vault-service");
        assert!(config.is_some());
        assert!(config
            .unwrap()
            .to_string_lossy()
            .contains("vault-service.ncl"));
    }

    #[test]
    fn test_team_dir() {
        let temp_dir = TempDir::new().unwrap();
        let resolver = TeamConfigResolver::new(temp_dir.path().to_path_buf());

        let team_dir = resolver.team_dir("team-a");
        assert!(team_dir.to_string_lossy().contains("teams/team-a"));
    }
}
