use std::path::PathBuf;
use std::process::Command;

use south::error::{SouthError, SouthResult};
use south::{repo_name_from_url, SouthManifest};

use crate::config::ConfigManager;

pub fn handle_south_new(
    project_dir: Option<PathBuf>,
    config_manager: &mut ConfigManager,
) -> SouthResult<()> {
    let project_dir = match project_dir {
        Some(dir) => {
            std::fs::create_dir_all(&dir).map_err(|e| {
                SouthError::IoError(format!("Error creating project directory: {}", e))
            })?;
            dir
        }
        None => std::env::current_dir().map_err(|e| {
            SouthError::IoError(format!("Error getting current directory: {}", e))
        })?,
    };

    let south_manifest = south::SouthManifestBuilder::new().finish();
    south_manifest.create_south_manifest(&project_dir)?;

    // Register workspace in config
    let name = project_dir
        .file_name()
        .map(|n| n.to_string_lossy().to_string())
        .unwrap_or_else(|| "unnamed".to_string());

    config_manager.create_config(crate::config::SouthConfig {
        name: name.clone(),
        path: project_dir.clone(),
    })?;

    println!("Workspace '{}' registered as active", name);
    Ok(())
}

pub fn handle_south_init(
    project_dir: Option<PathBuf>,
    config_manager: &mut ConfigManager,
) -> SouthResult<()> {
    let project_dir = match project_dir {
        Some(dir) => dir,
        None => std::env::current_dir().map_err(|e| {
            SouthError::IoError(format!("Error getting current directory: {}", e))
        })?,
    };

    // Verify south.yaml exists
    if !project_dir.join("south.yaml").exists() {
        return Err(SouthError::ConfigurationError(format!(
            "south.yaml not found in {:?}",
            project_dir
        )));
    }

    let name = project_dir
        .file_name()
        .map(|n| n.to_string_lossy().to_string())
        .unwrap_or_else(|| "unnamed".to_string());

    config_manager.create_config(crate::config::SouthConfig {
        name: name.clone(),
        path: project_dir.clone(),
    })?;

    println!("Workspace '{}' initialized and set as active", name);
    Ok(())
}

pub fn handle_south_update(config_manager: &ConfigManager) -> SouthResult<()> {
    let active = config_manager
        .get_active_config()
        .ok_or_else(|| {
            SouthError::ConfigurationError("No active workspace found".to_string())
        })?;

    let workspace_root = active.path.parent().ok_or_else(|| {
        SouthError::ConfigurationError("Invalid workspace path".to_string())
    })?;

    let manifest = SouthManifest::load(&active.path)?;

    if manifest.repos.is_empty() {
        println!("No repositories in manifest. Add one with `south add-repo`");
        return Ok(());
    }

    for repo in &manifest.repos {
        let dest = match &repo.path {
            Some(p) => workspace_root.join(p),
            None => workspace_root.join(&repo.name),
        };

        if south::git::is_git_repo(&dest) {
            println!("Updating {}...", repo.name);
            south::git::git_checkout(&dest, &repo.revision)?;
        } else {
            println!("Cloning {}...", repo.name);
            south::git::git_clone_at_revision(&repo.url, &dest, &repo.revision)?;
        }
    }

    println!("Update complete");
    Ok(())
}

pub fn handle_south_add(url: &str, config_manager: &mut ConfigManager) -> SouthResult<()> {
    let active = config_manager
        .get_active_config()
        .ok_or_else(|| {
            SouthError::ConfigurationError("No active workspace found".to_string())
        })?;

    let workspace_root = active.path.parent().ok_or_else(|| {
        SouthError::ConfigurationError("Invalid workspace path".to_string())
    })?;

    let mut manifest = SouthManifest::load(&active.path)?;

    // Check for duplicate URL
    if manifest.repos.iter().any(|r| r.url == url) {
        return Err(SouthError::ConfigurationError(format!(
            "Repository '{}' already exists in manifest",
            url
        )));
    }

    let name = repo_name_from_url(url);
    let dest = workspace_root.join(&name);

    // Clone the repo
    println!("Cloning {}...", name);
    south::git::git_clone(url, &dest)?;

    // Add to manifest
    manifest.repos.push(south::ManifestRepo::new(
        name.clone(),
        url.to_string(),
        "main".to_string(),
        None,
    ));

    manifest.save(&active.path)?;
    println!("Added '{}' to south.yaml", name);

    Ok(())
}

pub fn handle_south_clone(url: &str, config_manager: &mut ConfigManager) -> SouthResult<()> {
    let name = repo_name_from_url(url);
    let cwd = std::env::current_dir().map_err(|e| {
        SouthError::IoError(format!("Error getting current directory: {}", e))
    })?;

    let project_dir = cwd.join(&name);

    println!("Cloning project '{}'...", name);
    south::git::git_clone(url, &project_dir)?;

    // Verify south.yaml exists
    if !project_dir.join("south.yaml").exists() {
        return Err(SouthError::ConfigurationError(format!(
            "Cloned repository does not contain a south.yaml"
        )));
    }

    // Register workspace
    config_manager.create_config(crate::config::SouthConfig {
        name: name.clone(),
        path: project_dir.clone(),
    })?;

    println!("Workspace '{}' registered", name);

    // Run update to fetch dependencies
    handle_south_update(config_manager)?;

    Ok(())
}

pub fn handle_south_forall(command: &str, config_manager: &ConfigManager) -> SouthResult<()> {
    let active = config_manager
        .get_active_config()
        .ok_or_else(|| {
            SouthError::ConfigurationError("No active workspace found".to_string())
        })?;

    let workspace_root = active.path.parent().ok_or_else(|| {
        SouthError::ConfigurationError("Invalid workspace path".to_string())
    })?;

    let manifest = SouthManifest::load(&active.path)?;

    // Build list of all repo paths: project repo + dependency repos
    let mut repos = vec![(active.path.file_name().unwrap_or_default().to_string_lossy().to_string(), active.path.clone())];

    for repo in &manifest.repos {
        let dest = match &repo.path {
            Some(p) => workspace_root.join(p),
            None => workspace_root.join(&repo.name),
        };
        if dest.exists() {
            repos.push((repo.name.clone(), dest));
        }
    }

    let mut failed = 0;
    for (repo_name, repo_path) in &repos {
        println!("=== {} ===", repo_name);
        let output = Command::new("sh")
            .arg("-c")
            .arg(command)
            .current_dir(repo_path)
            .status();

        match output {
            Ok(status) if !status.success() => {
                eprintln!("Command failed in {}", repo_name);
                failed += 1;
            }
            Err(e) => {
                eprintln!("Failed to run command in {}: {}", repo_name, e);
                failed += 1;
            }
            _ => {}
        }
    }

    if failed > 0 {
        eprintln!("{} of {} repos had failures", failed, repos.len());
    }

    Ok(())
}
