//! F5 — String monomers: MC_40–MC_47
//!
//! Text processing. All operations are UTF-8 safe and length-bounded.
//! Maximum string length: 4096 bytes.

/// Maximum allowed string length in bytes.
pub const MAX_STR_LEN: usize = 4096;

/// MC_40 — CONCAT: Concatenate two strings, capped at MAX_STR_LEN.
///
/// ```
/// use brik64_core::mc::concat;
/// assert_eq!(concat("hello", " world"), "hello world");
/// ```
pub fn concat(a: &str, b: &str) -> String {
    let mut s = String::with_capacity((a.len() + b.len()).min(MAX_STR_LEN));
    s.push_str(a);
    let remaining = MAX_STR_LEN.saturating_sub(s.len());
    if remaining > 0 {
        let b_truncated = &b[..b.len().min(remaining)];
        s.push_str(b_truncated);
    }
    s
}

/// MC_41 — SUBSTR: Safe substring extraction.
///
/// Returns the substring `[start, start+len)` clipped to valid char boundaries.
/// Never panics on out-of-range indices.
///
/// ```
/// use brik64_core::mc::substr;
/// assert_eq!(substr("hello world", 6, 5), "world");
/// assert_eq!(substr("hi", 0, 100), "hi"); // clipped
/// ```
pub fn substr(s: &str, start: usize, len: usize) -> &str {
    if start >= s.len() {
        return "";
    }
    // Align to char boundary
    let start = s.floor_char_boundary(start);
    let end = s.floor_char_boundary((start + len).min(s.len()));
    &s[start..end]
}

/// MC_42 — TRIM: Remove leading and trailing whitespace.
pub fn trim(s: &str) -> &str {
    s.trim()
}

/// MC_43 — LEN: Length in bytes.
///
/// ```
/// use brik64_core::mc::len;
/// assert_eq!(len("hello"), 5);
/// ```
pub fn len(s: &str) -> usize {
    s.len()
}

/// MC_44 — CONTAINS: Check if string contains a substring.
pub fn contains(haystack: &str, needle: &str) -> bool {
    haystack.contains(needle)
}

/// MC_45 — STARTS_WITH: Check prefix.
pub fn starts_with(s: &str, prefix: &str) -> bool {
    s.starts_with(prefix)
}

/// MC_46 — ENDS_WITH: Check suffix.
pub fn ends_with(s: &str, suffix: &str) -> bool {
    s.ends_with(suffix)
}

/// MC_47 — CHAR_AT: Get byte at position. Returns 0 if out of bounds.
///
/// ```
/// use brik64_core::mc::char_at;
/// assert_eq!(char_at("hello", 1), b'e');
/// assert_eq!(char_at("hi", 99), 0);
/// ```
pub fn char_at(s: &str, index: usize) -> u8 {
    s.as_bytes().get(index).copied().unwrap_or(0)
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn substr_oob() {
        assert_eq!(substr("hi", 100, 5), "");
        assert_eq!(substr("hello", 2, 2), "ll");
    }
    #[test]
    fn concat_cap() {
        let big = "x".repeat(MAX_STR_LEN);
        let result = concat(&big, "overflow");
        assert!(result.len() <= MAX_STR_LEN);
    }
}
