//! Pure helpers for pipeline item ID parsing.
//!
//! Pipeline item IDs are numeric strings, e.g. `"42"`, `"730"`. Legacy items
//! may use the old `{number}_{type}_{slug}` format (e.g. `"42_story_foo"`).
//! The functions here extract or validate the leading numeric segment without
//! performing any I/O.

/// Extract the numeric prefix from a pipeline item ID.
///
/// Works for both numeric-only IDs (`"42"` → `"42"`) and legacy slug-format
/// IDs (`"42_story_foo"` → `"42"`).
/// Returns `None` if the ID has no leading digit sequence.
pub fn extract_item_number(item_id: &str) -> Option<&str> {
    item_id
        .split('_')
        .next()
        .filter(|s| !s.is_empty() && s.chars().all(|c| c.is_ascii_digit()))
}

#[allow(dead_code)]
/// Return `true` if `item_id` starts with a numeric prefix.
///
/// Valid: `"42"`, `"42_story_foo"`, `"1_bug_bar"`.
/// Invalid: `"story_without_number"`, `""`, `"abc_story"`.
pub fn has_valid_id_prefix(item_id: &str) -> bool {
    extract_item_number(item_id).is_some()
}

// ── Tests ─────────────────────────────────────────────────────────────────────

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

    #[test]
    fn extract_item_number_extracts_prefix() {
        assert_eq!(extract_item_number("42_story_foo"), Some("42"));
        assert_eq!(extract_item_number("1_bug_bar"), Some("1"));
        assert_eq!(extract_item_number("100_refactor_baz"), Some("100"));
        assert_eq!(
            extract_item_number("261_story_bot_notifications"),
            Some("261")
        );
        assert_eq!(extract_item_number("1_spike_research"), Some("1"));
    }

    #[test]
    fn extract_item_number_works_for_numeric_only_ids() {
        // Numeric-only IDs (the new canonical format).
        assert_eq!(extract_item_number("42"), Some("42"));
        assert_eq!(extract_item_number("730"), Some("730"));
        assert_eq!(extract_item_number("1"), Some("1"));
    }

    #[test]
    fn has_valid_id_prefix_returns_true_for_numeric_only() {
        assert!(has_valid_id_prefix("42"));
        assert!(has_valid_id_prefix("730"));
    }

    #[test]
    fn extract_item_number_returns_none_for_no_numeric_prefix() {
        assert_eq!(extract_item_number("story_without_number"), None);
        assert_eq!(extract_item_number("abc_story"), None);
        assert_eq!(extract_item_number("abc_story_thing"), None);
        assert_eq!(extract_item_number(""), None);
    }

    #[test]
    fn extract_item_number_returns_none_for_empty_first_segment() {
        // Leading underscore: first segment is "".
        assert_eq!(extract_item_number("_story_thing"), None);
    }

    #[test]
    fn has_valid_id_prefix_returns_true_for_valid_ids() {
        assert!(has_valid_id_prefix("42_story_foo"));
        assert!(has_valid_id_prefix("1_bug_bar"));
    }

    #[test]
    fn has_valid_id_prefix_returns_false_for_invalid_ids() {
        assert!(!has_valid_id_prefix("story_no_number"));
        assert!(!has_valid_id_prefix(""));
    }
}
