/* src/server/injector/rust/src/token.rs */

#[derive(Debug)]
pub(crate) enum Token {
	Text(String),
	Marker(String), // directive body (between <!--seam: and -->)
}

pub(crate) const MARKER_OPEN: &str = "<!--seam:";
pub(crate) const MARKER_CLOSE: &str = "-->";

pub(crate) fn tokenize(template: &str) -> Vec<Token> {
	let mut tokens = Vec::new();
	let mut pos = 0;
	let bytes = template.as_bytes();

	while pos < bytes.len() {
		if let Some(rel) = template[pos..].find(MARKER_OPEN) {
			let marker_start = pos + rel;
			if marker_start > pos {
				tokens.push(Token::Text(template[pos..marker_start].to_string()));
			}
			let after_open = marker_start + MARKER_OPEN.len();
			if let Some(close_rel) = template[after_open..].find(MARKER_CLOSE) {
				let directive = template[after_open..after_open + close_rel].to_string();
				tokens.push(Token::Marker(directive));
				pos = after_open + close_rel + MARKER_CLOSE.len();
			} else {
				// Unclosed marker -- treat rest as text
				tokens.push(Token::Text(template[marker_start..].to_string()));
				break;
			}
		} else {
			tokens.push(Token::Text(template[pos..].to_string()));
			break;
		}
	}

	tokens
}

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

	#[test]
	fn tokenize_empty_template() {
		let tokens = tokenize("");
		assert!(tokens.is_empty());
	}

	#[test]
	fn tokenize_plain_html() {
		let tokens = tokenize("<p>hello</p>");
		assert_eq!(tokens.len(), 1);
		assert!(matches!(&tokens[0], Token::Text(s) if s == "<p>hello</p>"));
	}

	#[test]
	fn tokenize_single_marker() {
		let tokens = tokenize("<!--seam:x-->");
		assert_eq!(tokens.len(), 1);
		assert!(matches!(&tokens[0], Token::Marker(s) if s == "x"));
	}

	#[test]
	fn tokenize_marker_at_start() {
		let tokens = tokenize("<!--seam:x-->tail");
		assert_eq!(tokens.len(), 2);
		assert!(matches!(&tokens[0], Token::Marker(s) if s == "x"));
		assert!(matches!(&tokens[1], Token::Text(s) if s == "tail"));
	}

	#[test]
	fn tokenize_marker_at_end() {
		let tokens = tokenize("head<!--seam:x-->");
		assert_eq!(tokens.len(), 2);
		assert!(matches!(&tokens[0], Token::Text(s) if s == "head"));
		assert!(matches!(&tokens[1], Token::Marker(s) if s == "x"));
	}

	#[test]
	fn tokenize_adjacent_markers() {
		let tokens = tokenize("<!--seam:a--><!--seam:b-->");
		assert_eq!(tokens.len(), 2);
		assert!(matches!(&tokens[0], Token::Marker(s) if s == "a"));
		assert!(matches!(&tokens[1], Token::Marker(s) if s == "b"));
	}

	#[test]
	fn tokenize_unclosed_marker() {
		let tokens = tokenize("<!--seam:x");
		assert_eq!(tokens.len(), 1);
		assert!(matches!(&tokens[0], Token::Text(s) if s == "<!--seam:x"));
	}

	#[test]
	fn tokenize_empty_directive() {
		let tokens = tokenize("<!--seam:-->");
		assert_eq!(tokens.len(), 1);
		assert!(matches!(&tokens[0], Token::Marker(s) if s.is_empty()));
	}
}
