use std::{borrow::Cow, fmt, str::FromStr};

use crate::prelude::*;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CommandId {
    pub group: Cow<'static, str>,
    pub name: Cow<'static, str>,
}

impl CommandId {
    /// Zero-allocation constructor for static strings known at compile time.
    pub const fn new_static(group: &'static str, name: &'static str) -> Self {
        Self {
            group: Cow::Borrowed(group),
            name: Cow::Borrowed(name),
        }
    }
}

impl fmt::Display for CommandId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}.{}", self.group, self.name)
    }
}

/// Parse `"group.name"` into a `CommandId`.
/// Panics at the call site if the string is malformed — acceptable for static
/// registration code that is always developer-controlled.
impl From<&'static str> for CommandId {
    fn from(s: &'static str) -> Self {
        match s.splitn(2, '.').collect::<Vec<_>>().as_slice() {
            [group, name] => Self::new_static(group, name),
            _ => panic!("CommandId must be 'group.name', got: {:?}", s),
        }
    }
}

impl From<(&'static str, &'static str)> for CommandId {
    fn from(value: (&'static str, &'static str)) -> Self {
        Self::new_static(value.0, value.1)
    }
}

impl FromStr for CommandId {
    type Err = Error;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        match s.splitn(2, '.').collect::<Vec<_>>().as_slice() {
            [group, name] => Ok(Self {
                group: Cow::Owned((*group).to_owned()),
                name: Cow::Owned((*name).to_owned()),
            }),
            _ => Err(anyhow!("CommandId must be 'group.name', got: {:?}", s)),
        }
    }
}
