//! Build script detection — generates `script/build` content for new projects.

use std::path::Path;

use super::detect_node_build_cmd;

/// Generate `script/build` content for a new project at `root`.
///
/// Inspects well-known marker files to identify which tech stacks are present
/// and emits the appropriate build commands. Multi-stack projects get combined
/// commands run sequentially. Falls back to a generic stub when no markers
/// are found so the scaffold is always valid.
///
/// For projects with a frontend in a known subdirectory (`frontend/`, `client/`),
/// the build command is detected from the presence of `pnpm-lock.yaml`.
pub(crate) fn detect_script_build(root: &Path) -> String {
    let mut commands: Vec<String> = Vec::new();

    if root.join("Cargo.toml").exists() {
        commands.push("cargo build --release".to_string());
    }

    if root.join("package.json").exists() {
        commands.push(detect_node_build_cmd(root));
    }

    // Detect frontend in known subdirectories (e.g. frontend/, client/)
    for subdir in &["frontend", "client"] {
        let sub_path = root.join(subdir);
        if sub_path.join("package.json").exists() {
            let cmd = detect_node_build_cmd(&sub_path);
            commands.push(format!("(cd {} && {})", subdir, cmd));
        }
    }

    if root.join("pyproject.toml").exists() {
        commands.push("python -m build".to_string());
    }

    if root.join("go.mod").exists() {
        commands.push("go build ./...".to_string());
    }

    if commands.is_empty() {
        return "#!/usr/bin/env bash\nset -euo pipefail\n\n# Add your project's build commands here.\necho \"No build configured\"\n".to_string();
    }

    let mut script = "#!/usr/bin/env bash\nset -euo pipefail\n\n".to_string();
    for cmd in commands {
        script.push_str(&cmd);
        script.push('\n');
    }
    script
}

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

    #[test]
    fn detect_script_build_no_markers_returns_stub() {
        let dir = tempdir().unwrap();
        let script = detect_script_build(dir.path());
        assert!(
            script.contains("No build configured"),
            "fallback should contain the generic stub message"
        );
        assert!(script.starts_with("#!/usr/bin/env bash"));
    }

    #[test]
    fn detect_script_build_cargo_toml_adds_cargo_build_release() {
        let dir = tempdir().unwrap();
        fs::write(dir.path().join("Cargo.toml"), "[package]\nname = \"x\"\n").unwrap();

        let script = detect_script_build(dir.path());
        assert!(
            script.contains("cargo build --release"),
            "Rust project should run cargo build --release"
        );
        assert!(!script.contains("No build configured"));
    }

    #[test]
    fn detect_script_build_package_json_npm_adds_npm_run_build() {
        let dir = tempdir().unwrap();
        fs::write(dir.path().join("package.json"), "{}").unwrap();

        let script = detect_script_build(dir.path());
        assert!(
            script.contains("npm run build"),
            "Node project without pnpm-lock should run npm run build"
        );
    }

    #[test]
    fn detect_script_build_package_json_pnpm_adds_pnpm_run_build() {
        let dir = tempdir().unwrap();
        fs::write(dir.path().join("package.json"), "{}").unwrap();
        fs::write(dir.path().join("pnpm-lock.yaml"), "").unwrap();

        let script = detect_script_build(dir.path());
        assert!(
            script.contains("pnpm run build"),
            "Node project with pnpm-lock should run pnpm run build"
        );
        assert!(
            !script.lines().any(|l| l.trim() == "npm run build"),
            "should not use npm when pnpm-lock.yaml is present"
        );
    }

    #[test]
    fn detect_script_build_go_mod_adds_go_build() {
        let dir = tempdir().unwrap();
        fs::write(dir.path().join("go.mod"), "module example.com/app\n").unwrap();

        let script = detect_script_build(dir.path());
        assert!(
            script.contains("go build ./..."),
            "Go project should run go build ./..."
        );
    }

    #[test]
    fn detect_script_build_pyproject_toml_adds_python_build() {
        let dir = tempdir().unwrap();
        fs::write(
            dir.path().join("pyproject.toml"),
            "[project]\nname = \"x\"\n",
        )
        .unwrap();

        let script = detect_script_build(dir.path());
        assert!(
            script.contains("python -m build"),
            "Python project should run python -m build"
        );
    }

    #[test]
    fn detect_script_build_frontend_subdir_detected() {
        let dir = tempdir().unwrap();
        let frontend = dir.path().join("frontend");
        fs::create_dir_all(&frontend).unwrap();
        fs::write(frontend.join("package.json"), "{}").unwrap();

        let script = detect_script_build(dir.path());
        assert!(
            script.contains("cd frontend"),
            "frontend subdir should be detected for build"
        );
        assert!(script.contains("npm run build"));
    }

    #[test]
    fn detect_script_build_rust_plus_frontend_subdir_both_included() {
        let dir = tempdir().unwrap();
        fs::write(
            dir.path().join("Cargo.toml"),
            "[package]\nname = \"server\"\n",
        )
        .unwrap();
        let frontend = dir.path().join("frontend");
        fs::create_dir_all(&frontend).unwrap();
        fs::write(frontend.join("package.json"), "{}").unwrap();

        let script = detect_script_build(dir.path());
        assert!(script.contains("cargo build --release"));
        assert!(script.contains("cd frontend"));
        assert!(script.contains("npm run build"));
    }
}
