//! Pre-lock validation helpers for `AgentPool::start_agent`.

use std::path::Path;

use super::super::super::{
    PipelineStage, agent_config_stage, canonical_pipeline_stage, pipeline_stage,
};
use super::super::worktree::find_active_story_stage;
use crate::config::ProjectConfig;

/// Validate that an explicit `agent_name` is allowed to attach to `story_id`'s
/// current pipeline stage.
///
/// Prevents wrong-stage assignments like a mergemaster on a coding-stage story
/// (bug 312).  Returns `Ok(())` if the agent has no specific stage (e.g.
/// supervisor) or the story is not in an active stage; `Err` with a descriptive
/// message on a stage mismatch.
pub(super) fn validate_agent_stage(
    config: &ProjectConfig,
    project_root: &Path,
    story_id: &str,
    agent_name: Option<&str>,
) -> Result<(), String> {
    let Some(name) = agent_name else {
        return Ok(());
    };
    let agent_stage = config
        .find_agent(name)
        .map(agent_config_stage)
        .unwrap_or_else(|| pipeline_stage(name));
    if agent_stage == PipelineStage::Other {
        return Ok(());
    }
    let Some(story_stage) = find_active_story_stage(project_root, story_id) else {
        return Ok(());
    };
    let canonical = canonical_pipeline_stage(&story_stage);
    let is_llm = matches!(
        agent_stage,
        PipelineStage::Coder | PipelineStage::Qa | PipelineStage::Mergemaster
    );
    if is_llm && (canonical.is_none() || canonical.as_ref() != Some(&agent_stage)) {
        return Err(format!(
            "Agent '{name}' (stage: {agent_stage:?}) cannot be assigned to \
             story '{story_id}' in {}/ (requires stage: {canonical:?})",
            story_stage.dir_name()
        ));
    }
    Ok(())
}

/// Read the preferred `agent:` field from the story's front matter.
///
/// When `agent_name` is `None` (caller is auto-selecting), this lets
/// `start_agent` honour an explicit `agent: coder-opus` written by the
/// `assign` command (bug 379).  Returns `None` when an explicit agent_name
/// was already supplied or when the story has no front-matter preference.
pub(super) fn read_front_matter_agent(
    story_id: &str,
    agent_name: Option<&str>,
) -> Option<crate::config::AgentName> {
    if agent_name.is_some() {
        return None;
    }
    // Story 929: the agent pin lives in the CRDT typed register; the
    // legacy YAML fallback is gone — post-891 every story has its CRDT
    // entry and any story without one has no pinned agent.
    crate::crdt_state::read_item(story_id).and_then(|w| w.agent())
}
