import "../../runtime/eltest.el"
// test_string.el - native test suite for runtime/string.el
//
// Covers: type conversions, core primitives, comparison and search,
// case conversion, whitespace trimming, replacement, repetition,
// reversal, prefix/suffix stripping, padding, counting, character
// classification, splitting, and joining.

test "int-to-str" {
    let s: String = int_to_str(42)
    assert s == "42", "int 42 to string"
    let neg: String = int_to_str(-7)
    assert neg == "-7", "negative int to string"
    let zero: String = int_to_str(0)
    assert zero == "0", "zero to string"
}

test "str-to-int" {
    let n: Int = str_to_int("123")
    assert n == 123, "parse 123"
    let neg: Int = str_to_int("-5")
    assert neg == -5, "parse negative"
    let zero: Int = str_to_int("0")
    assert zero == 0, "parse zero"
}

test "bool-to-str" {
    let t: String = bool_to_str(true)
    assert t == "true", "true to string"
    let f: String = bool_to_str(false)
    assert f == "false", "false to string"
}

test "str-len" {
    let n: Int = str_len("hello")
    assert n == 5, "length of hello"
    let e: Int = str_len("")
    assert e == 0, "length of empty string"
    let space: Int = str_len("a b")
    assert space == 3, "length with spaces"
}

test "str-eq" {
    assert str_eq("abc", "abc"), "identical strings are equal"
    assert !str_eq("abc", "ABC"), "case-sensitive comparison"
    assert str_eq("", ""), "empty strings are equal"
    assert !str_eq("a", "b"), "different single chars"
}

test "str-slice" {
    let s: String = str_slice("hello world", 6, 11)
    assert s == "world", "slice end of string"
    let start: String = str_slice("hello world", 0, 5)
    assert start == "hello", "slice start of string"
    let empty: String = str_slice("hello", 2, 2)
    assert empty == "", "zero-length slice"
}

test "str-starts-ends-with" {
    assert str_starts_with("hello world", "hello"), "starts with hello"
    assert str_ends_with("hello world", "world"), "ends with world"
    assert !str_starts_with("hello world", "world"), "does not start with world"
    assert !str_ends_with("hello world", "hello"), "does not end with hello"
    assert str_starts_with("abc", ""), "empty prefix always matches"
    assert str_ends_with("abc", ""), "empty suffix always matches"
}

test "str-contains" {
    assert str_contains("hello world", "world"), "contains world"
    assert str_contains("hello world", "lo wo"), "contains interior substring"
    assert !str_contains("hello world", "xyz"), "does not contain xyz"
    assert str_contains("abc", ""), "empty sub is always contained"
    assert !str_contains("", "x"), "empty string does not contain nonempty sub"
}

test "str-index-of" {
    let i: Int = str_index_of("hello world", "world")
    assert i == 6, "index of world"
    let j: Int = str_index_of("hello world", "xyz")
    assert j == -1, "not found returns -1"
    let k: Int = str_index_of("aabbcc", "bb")
    assert k == 2, "index of bb in aabbcc"
}

test "str-last-index-of" {
    let i: Int = str_last_index_of("abcabc", "bc")
    assert i == 4, "last occurrence of bc"
    let j: Int = str_last_index_of("hello", "xyz")
    assert j == -1, "not found returns -1"
    let k: Int = str_last_index_of("aaa", "a")
    assert k == 2, "last single-char match"
}

test "str-to-upper-lower" {
    let up: String = str_to_upper("hello")
    assert up == "HELLO", "to upper"
    let lo: String = str_to_lower("WORLD")
    assert lo == "world", "to lower"
    let mixed: String = str_to_upper("Hello World")
    assert mixed == "HELLO WORLD", "mixed to upper"
    let empty: String = str_to_lower("")
    assert empty == "", "empty stays empty"
}

test "str-trim" {
    let s: String = str_trim("  hello  ")
    assert s == "hello", "trim both ends"
    let lonly: String = str_trim("  hello")
    assert lonly == "hello", "trim left only"
    let ronly: String = str_trim("hello  ")
    assert ronly == "hello", "trim right only"
    let tabs: String = str_trim("\thello\n")
    assert tabs == "hello", "trim tabs and newlines"
    let empty: String = str_trim("   ")
    assert empty == "", "all whitespace trims to empty"
}

test "str-replace" {
    let s: String = str_replace("hello world", "world", "there")
    assert s == "hello there", "replace word"
    let none: String = str_replace("hello", "xyz", "abc")
    assert none == "hello", "no match leaves string unchanged"
    let multi: String = str_replace("aaa", "a", "b")
    assert multi == "bbb", "replace all occurrences"
    let empty_from: String = str_replace("hello", "", "x")
    assert empty_from == "hello", "empty from returns original"
}

test "str-repeat" {
    let s: String = str_repeat("ab", 3)
    assert s == "ababab", "repeat 3 times"
    let once: String = str_repeat("x", 1)
    assert once == "x", "repeat once"
    let zero: String = str_repeat("abc", 0)
    assert zero == "", "repeat zero times"
    let neg: String = str_repeat("abc", -1)
    assert neg == "", "negative repeat is empty"
}

test "str-reverse" {
    let s: String = str_reverse("hello")
    assert s == "olleh", "reverse hello"
    let single: String = str_reverse("a")
    assert single == "a", "reverse single char"
    let empty: String = str_reverse("")
    assert empty == "", "reverse empty string"
    let palindrome: String = str_reverse("racecar")
    assert palindrome == "racecar", "reverse palindrome"
}

test "str-strip-prefix-suffix" {
    let p: String = str_strip_prefix("foobar", "foo")
    assert p == "bar", "strip prefix foo"
    let no_prefix: String = str_strip_prefix("foobar", "baz")
    assert no_prefix == "foobar", "no match leaves string unchanged"
    let s: String = str_strip_suffix("hello.md", ".md")
    assert s == "hello", "strip suffix .md"
    let no_suffix: String = str_strip_suffix("hello.md", ".txt")
    assert no_suffix == "hello.md", "non-matching suffix unchanged"
}

test "str-strip-chars" {
    let s: String = str_strip_chars(" \thello \n", " \t\n")
    assert s == "hello", "strip whitespace chars"
    let dots: String = str_strip_chars("...hello...", ".")
    assert dots == "hello", "strip dot chars"
    let all: String = str_strip_chars("aaa", "a")
    assert all == "", "strip all chars leaves empty"
}

test "str-pad-left-right" {
    let l: String = str_pad_left("42", 5, "0")
    assert l == "00042", "zero-pad left to width 5"
    let r: String = str_pad_right("hi", 5, "-")
    assert r == "hi---", "dash-pad right to width 5"
    let no_pad: String = str_pad_left("hello", 3, "x")
    assert no_pad == "hello", "no pad when string already wide enough"
}

test "str-count" {
    let n: Int = str_count("abc abc abc", "abc")
    assert n == 3, "count three occurrences"
    let overlap: Int = str_count("aaa", "aa")
    assert overlap == 1, "non-overlapping count"
    let zero: Int = str_count("hello", "xyz")
    assert zero == 0, "not found gives 0"
    let empty_sub: Int = str_count("hello", "")
    assert empty_sub == 0, "empty sub gives 0"
}

test "str-count-lines-words-letters" {
    let s: String = "hello world\nfoo bar"
    let lines: Int = str_count_lines(s)
    let words: Int = str_count_words(s)
    let letters: Int = str_count_letters(s)
    assert lines == 2, "line count"
    assert words == 4, "word count"
    assert letters == 16, "letter count"
}

test "str-count-chars-and-digits" {
    let digits: Int = str_count_digits("abc123def456")
    assert digits == 6, "six digits"
    let none: Int = str_count_digits("hello")
    assert none == 0, "no digits"
    let chars: Int = str_count_chars("hello")
    assert chars == 5, "five ASCII chars"
}

test "char-classes" {
    assert is_letter("A"), "A is a letter"
    assert is_digit("7"), "7 is a digit"
    assert is_whitespace(" "), "space is whitespace"
    assert !is_letter("3"), "3 is not a letter"
    assert !is_digit("X"), "X is not a digit"
    assert is_alphanumeric("abc123"), "abc123 is alphanumeric"
    assert !is_alphanumeric("abc!"), "abc! is not alphanumeric"
    assert is_uppercase("ABC"), "ABC is uppercase"
    assert is_lowercase("abc"), "abc is lowercase"
    assert !is_uppercase("Abc"), "mixed is not uppercase"
}

test "str-split" {
    let parts: [String] = str_split("a,b,c", ",")
    let n: Int = native_list_len(parts)
    assert n == 3, "split into 3 parts"
    let p0: String = native_list_get(parts, 0)
    let p1: String = native_list_get(parts, 1)
    let p2: String = native_list_get(parts, 2)
    assert p0 == "a", "first part"
    assert p1 == "b", "second part"
    assert p2 == "c", "third part"
}

test "str-split-lines" {
    let lines: [String] = str_split_lines("alpha\nbeta\r\ngamma\n")
    let n: Int = native_list_len(lines)
    assert n == 3, "split into 3 lines"
    let l0: String = native_list_get(lines, 0)
    let l1: String = native_list_get(lines, 1)
    let l2: String = native_list_get(lines, 2)
    assert l0 == "alpha", "first line"
    assert l1 == "beta", "second line strips CR"
    assert l2 == "gamma", "third line"
}

test "str-join" {
    let parts: [String] = native_list_empty()
    let parts = native_list_append(parts, "alpha")
    let parts = native_list_append(parts, "beta")
    let parts = native_list_append(parts, "gamma")
    let result: String = str_join(parts, ", ")
    assert result == "alpha, beta, gamma", "join with separator"
    let empty_parts: [String] = native_list_empty()
    let empty_result: String = str_join(empty_parts, ",")
    assert empty_result == "", "join empty list gives empty string"
}

test "str-char-at" {
    let c: String = str_char_at("hello", 1)
    assert c == "e", "char at index 1"
    let first: String = str_char_at("abc", 0)
    assert first == "a", "first char"
    let oob: String = str_char_at("abc", 10)
    assert oob == "", "out of bounds returns empty"
}

test "url-encode-decode" {
    let encoded: String = url_encode("hello world")
    assert !str_contains(encoded, " "), "space is encoded"
    let decoded: String = url_decode(encoded)
    assert decoded == "hello world", "round-trip decode"
}
