//! Shell I/O — the ONLY place in `service::shell/` that may perform side effects.
//!
//! Side effects here include: filesystem existence and canonicalization checks,
//! process spawning via `std::process::Command`, and reading pipe output.
//! All pure logic (pattern matching, output truncation, count parsing) lives in
//! `path_guard.rs`.

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

/// Validate that `working_dir` is an absolute path that exists on disk and
/// lies inside the project's `.huskies/worktrees/` or `.huskies/merge_workspace/`
/// directory. Returns the canonicalized path on success.
///
/// # Errors
/// - [`Error::Validation`] if the path is relative or does not exist.
/// - [`Error::PathNotAllowed`] if the path is outside the allowed roots.
/// - [`Error::Io`] if canonicalization fails.
pub fn validate_working_dir(working_dir: &str, project_root: &Path) -> Result<PathBuf, Error> {
    let wd = PathBuf::from(working_dir);

    if !wd.is_absolute() {
        return Err(Error::Validation(
            "working_dir must be an absolute path".to_string(),
        ));
    }
    if !wd.exists() {
        return Err(Error::Validation(format!(
            "working_dir does not exist: {working_dir}"
        )));
    }

    let worktrees_root = project_root.join(".huskies").join("worktrees");

    let canonical_wd = wd
        .canonicalize()
        .map_err(|e| Error::Io(format!("Cannot canonicalize working_dir: {e}")))?;

    let canonical_wt = if worktrees_root.exists() {
        worktrees_root
            .canonicalize()
            .map_err(|e| Error::Io(format!("Cannot canonicalize worktrees root: {e}")))?
    } else {
        return Err(Error::PathNotAllowed(
            "No worktrees directory found in project".to_string(),
        ));
    };

    // Also allow the merge workspace so mergemaster can fix conflicts.
    let merge_workspace = project_root.join(".huskies").join("merge_workspace");
    let canonical_mw = merge_workspace.canonicalize().unwrap_or_default();

    let in_worktrees = canonical_wd.starts_with(&canonical_wt);
    let in_merge_ws =
        !canonical_mw.as_os_str().is_empty() && canonical_wd.starts_with(&canonical_mw);

    if !in_worktrees && !in_merge_ws {
        return Err(Error::PathNotAllowed(format!(
            "working_dir must be inside .huskies/worktrees/ or .huskies/merge_workspace/. Got: {working_dir}"
        )));
    }

    Ok(canonical_wd)
}
