//! Typed agent-name enum mirroring the `agents.toml` roster.
//!
//! `AgentName` is the single source of truth for valid agent identifiers.
//! Adding a new agent to `agents.toml` without a corresponding variant here
//! causes server startup to fail — `validate_agents` calls `AgentName::from_str`
//! for every configured agent name and returns an error for any unknown name.

use std::fmt;
use std::str::FromStr;

/// The set of named agents defined in `agents.toml`.
///
/// Variants mirror the `name` field of each `[[agent]]` entry.  Serialises to
/// the canonical dash-form string (e.g. `"coder-1"`).
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum AgentName {
    /// `name = "coder-1"`
    Coder1,
    /// `name = "coder-2"`
    Coder2,
    /// `name = "coder-3"`
    Coder3,
    /// `name = "coder-opus"`
    CoderOpus,
    /// `name = "qa"`
    Qa,
    /// `name = "qa-2"`
    Qa2,
    /// `name = "mergemaster"`
    Mergemaster,
}

impl AgentName {
    /// The canonical string form, matching the `name` field in `agents.toml`.
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Coder1 => "coder-1",
            Self::Coder2 => "coder-2",
            Self::Coder3 => "coder-3",
            Self::CoderOpus => "coder-opus",
            Self::Qa => "qa",
            Self::Qa2 => "qa-2",
            Self::Mergemaster => "mergemaster",
        }
    }

    /// All known variants, in agents.toml declaration order.
    pub fn all() -> &'static [AgentName] {
        &[
            Self::Coder1,
            Self::Coder2,
            Self::Coder3,
            Self::CoderOpus,
            Self::Qa,
            Self::Qa2,
            Self::Mergemaster,
        ]
    }
}

impl fmt::Display for AgentName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}

impl FromStr for AgentName {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "coder-1" => Ok(Self::Coder1),
            "coder-2" => Ok(Self::Coder2),
            "coder-3" => Ok(Self::Coder3),
            "coder-opus" => Ok(Self::CoderOpus),
            "qa" => Ok(Self::Qa),
            "qa-2" => Ok(Self::Qa2),
            "mergemaster" => Ok(Self::Mergemaster),
            other => Err(format!(
                "unknown agent name '{other}'; add a variant to AgentName or update agents.toml"
            )),
        }
    }
}

impl serde::Serialize for AgentName {
    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        s.serialize_str(self.as_str())
    }
}

impl<'de> serde::Deserialize<'de> for AgentName {
    fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
        let s = String::deserialize(d)?;
        Self::from_str(&s).map_err(serde::de::Error::custom)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn round_trip_all_variants() {
        for &name in AgentName::all() {
            let s = name.as_str();
            let parsed: AgentName = s.parse().expect("parse should succeed");
            assert_eq!(parsed, name);
            assert_eq!(parsed.to_string(), s);
        }
    }

    #[test]
    fn unknown_name_returns_err() {
        assert!("unknown-agent".parse::<AgentName>().is_err());
        assert!("".parse::<AgentName>().is_err());
    }

    #[test]
    fn display_matches_as_str() {
        assert_eq!(AgentName::Coder1.to_string(), "coder-1");
        assert_eq!(AgentName::CoderOpus.to_string(), "coder-opus");
        assert_eq!(AgentName::Mergemaster.to_string(), "mergemaster");
    }
}
