//! Pure story validation helpers for `service::story`.
//!
//! These functions validate story content and metadata without performing
//! any I/O.

#[allow(dead_code)]
/// Return `true` if `name` is a valid story name.
///
/// A valid name must contain at least one alphanumeric character (letters or
/// digits). Pure punctuation, symbols, or blank names are rejected.
pub fn is_valid_story_name(name: &str) -> bool {
    name.chars().any(|c| c.is_alphanumeric())
}

#[allow(dead_code)]
/// Validate that `criterion_index` is within bounds for `total_criteria`.
///
/// Returns `Ok(())` if valid, or `Err(message)` if out of range.
pub fn validate_criterion_index(
    criterion_index: usize,
    total_criteria: usize,
) -> Result<(), String> {
    if criterion_index >= total_criteria {
        Err(format!(
            "criterion_index {criterion_index} is out of range — story has {total_criteria} criteria (0-based)"
        ))
    } else {
        Ok(())
    }
}

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

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

    #[test]
    fn is_valid_story_name_accepts_alphanumeric() {
        assert!(is_valid_story_name("My Story"));
        assert!(is_valid_story_name("story123"));
        assert!(is_valid_story_name("Test"));
    }

    #[test]
    fn is_valid_story_name_rejects_symbols_only() {
        assert!(!is_valid_story_name("!!!"));
        assert!(!is_valid_story_name("---"));
        assert!(!is_valid_story_name(""));
    }

    #[test]
    fn validate_criterion_index_in_range() {
        assert!(validate_criterion_index(0, 3).is_ok());
        assert!(validate_criterion_index(2, 3).is_ok());
    }

    #[test]
    fn validate_criterion_index_out_of_range() {
        let e = validate_criterion_index(3, 3).unwrap_err();
        assert!(e.contains("out of range"));
        assert!(e.contains("3"));
    }

    #[test]
    fn validate_criterion_index_zero_criteria() {
        let e = validate_criterion_index(0, 0).unwrap_err();
        assert!(e.contains("out of range"));
    }
}
