extend("test")
extend("string")
extend("array")

describe("string:split", () => {
  it("splits a string by a separator", () => {
    expect_equal(string:split(",", "a,b,c"), ["a","b","c"])
  })
  it("splits with missing separator yields full string", () => {
    expect_equal(string:split("|", "abc"), ["abc"])
  })
  it("partial usage: fix separator", () => {
    splitComma = string:split(",")
    expect_equal(splitComma("a,b,c"), ["a","b","c"])
  })
  it("placeholder: string known", () => {
    partial = string:split(_, "x:y:z")
    expect_equal(partial(":"), ["x","y","z"])
  })
  it("splits an empty string", () => {
    expect_equal(string:split(",", ""), [""])
  })
  it("splits by empty separator to chars", () => {
    expect_equal(string:split("", "abc"), ["a","b","c"])
  })
})

describe("string:join", () => {
  it("joins a str-array with a separator", () => {
    expect_equal(string:join("-", ["foo","bar","baz"]), "foo-bar-baz")
  })
  it("partial usage: fix separator", () => {
    joinColon = string:join(":")
    expect_equal(joinColon(["x","y","z"]), "x:y:z")
  })
  it("placeholder: array known", () => {
    arrPartial = string:join(_, ["a","b","c"])
    expect_equal(arrPartial("."), "a.b.c")
  })
  it("joins a single-element array", () => {
    expect_equal(string:join(",", ["hi"]), "hi")
  })
  it("joins an empty array", () => {
    expect_equal(string:join(",", []), "")
  })
})

describe("string:trim", () => {
  it("trims leading and trailing spaces", () => {
    expect_equal(string:trim("   x y z  "), "x y z")
  })
  it("does nothing if no whitespace", () => {
    expect_equal(string:trim("foo"), "foo")
  })
  it("trims tabs and newlines", () => {
    expect_equal(string:trim("\n\t hi there \n"), "hi there")
  })
  it("trims empty string to empty", () => {
    expect_equal(string:trim(""), "")
  })
})

describe("string:replace", () => {
  it("replaces all substrings", () => {
    expect_equal(string:replace("o", "X", "foo foo"), "fXX fXX")
  })
  it("does nothing if not found", () => {
    expect_equal(string:replace("z", "*", "hello"), "hello")
  })
  it("partial usage: search string only", () => {
    rep = string:replace("l")
    expect_equal(rep("Y")("hello"), "heYYo")
  })
  it("placeholder: input string known", () => {
    part = string:replace(_, "Q", "abcabc")
    expect_equal(part("a"), "QbcQbc")
  })
  it("replaces empty string inserts replacement between chars", () => {
    expect_equal(string:replace("", ".", "hi"), ".h.i.")
  })
  it("replaces with empty string removes substring", () => {
    expect_equal(string:replace("x", "", "xoxo"), "oo")
  })
})

describe("string:contains", () => {
  it("returns true if substring is present", () => {
    expect_equal(string:contains("ll", "hello"), true)
  })
  it("returns false if substring not present", () => {
    expect_equal(string:contains("z", "hello"), false)
  })
  it("partial usage: fix needle", () => {
    containsE = string:contains("e")
    expect_equal(containsE("one"), true)
    expect_equal(containsE("two"), false)
  })
  it("placeholder: haystack known", () => {
    part = string:contains(_, "abcabc")
    expect_equal(part("bca"), true)
    expect_equal(part("zzz"), false)
  })
  it("empty substring always returns true", () => {
    expect_equal(string:contains("", "any"), true)
  })
})

describe("string:starts_with", () => {
  it("returns true if string starts with prefix", () => {
    expect_equal(string:starts_with("Hel", "Hello"), true)
  })
  it("returns false if not", () => {
    expect_equal(string:starts_with("X", "abc"), false)
  })
  it("partial usage: fix prefix", () => {
    sw = string:starts_with("pre")
    expect_equal(sw("prefixing"), true)
    expect_equal(sw("example"), false)
  })
  it("placeholder: string known", () => {
    p = string:starts_with(_, "xyz")
    expect_equal(p("x"), true)
    expect_equal(p("y"), false)
  })
})

describe("string:ends_with", () => {
  it("returns true if string ends with suffix", () => {
    expect_equal(string:ends_with("lo", "hello"), true)
  })
  it("returns false if not", () => {
    expect_equal(string:ends_with("z", "buzz"), false)
  })
  it("partial usage: fix suffix", () => {
    ew = string:ends_with("bar")
    expect_equal(ew("foobar"), true)
    expect_equal(ew("foo"), false)
  })
  it("placeholder: string known", () => {
    p = string:ends_with(_, "abcde")
    expect_equal(p("de"), true)
    expect_equal(p("c"), false)
  })
})

describe("string:slice", () => {
  it("returns substring from start to end", () => {
    expect_equal(string:slice("abcdef", 1, 4), "bcd")
  })
  it("negative indices count from end", () => {
    expect_equal(string:slice("abcdef", -3, -1), "de")
  })
  it("partial usage: start and end known", () => {
    slicer = string:slice(_, 2, 5)
    expect_equal(slicer("abcdefg"), "cde")
  })
  it("handles out-of-bounds gracefully", () => {
    expect_equal(string:slice("abc", 0, 10), "abc")
    expect_equal(string:slice("abc", 2, 1), "")
  })
  it("slice empty string returns empty", () => {
    expect_equal(string:slice("", 0, 2), "")
  })
})

describe("string:repeat", () => {
  it("repeats a string n times", () => {
    expect_equal(string:repeat("ha", 3), "hahaha")
  })
  it("repeat 0 times is empty", () => {
    expect_equal(string:repeat("boom", 0), "")
  })
  it("partial usage: string known", () => {
    triple = string:repeat("x")
    expect_equal(triple(4), "xxxx")
  })
  it("placeholder: count known", () => {
    part = string:repeat(_, 2)
    expect_equal(part("bar"), "barbar")
  })
})

describe("string:index_of", () => {
  it("finds first index of substring", () => {
    expect_equal(string:index_of("l", "hello"), 2)
    expect_equal(string:index_of("lo", "hello"), 3)
  })
  it("returns -1 if not found", () => {
    expect_equal(string:index_of("x", "abc"), -1)
  })
  it("partial usage: fix needle", () => {
    idxOfE = string:index_of("e")
    expect_equal(idxOfE("eagle"), 0)
    expect_equal(idxOfE("lion"), -1)
  })
  it("placeholder: haystack known", () => {
    p = string:index_of(_, "mississippi")
    expect_equal(p("ss"), 2)
    expect_equal(p("zz"), -1)
  })
})

describe("string:last_index_of", () => {
  it("finds last index of substring", () => {
    expect_equal(string:last_index_of("l", "hello lol"), 7)
    expect_equal(string:last_index_of("o", "foo"), 2)
  })
  it("returns -1 if not found", () => {
    expect_equal(string:last_index_of("q", "abc"), -1)
  })
  it("partial usage: fix needle", () => {
    lastE = string:last_index_of("e")
    expect_equal(lastE("cheese"), 5)
    expect_equal(lastE("abc"), -1)
  })
  it("placeholder: haystack known", () => {
    p = string:last_index_of(_, "banana")
    expect_equal(p("a"), 5)
    expect_equal(p("x"), -1)
  })
})

# Error handling for type errors
describe("string functions error cases", () => {
  it("errors on string:split non-string", () => {
    test:expect_error(() => string:split(",", 42))
    test:expect_error(() => string:split(123, ["a","b"]))
  })
  it("errors on string:join non-array", () => {
    test:expect_error(() => string:join(",", 999))
    test:expect_error(() => string:join(",", "not_array"))
  })
  it("errors on string:trim non-string", () => {
    test:expect_error(() => string:trim([1,2,3]))
    test:expect_error(() => string:trim(123))
  })
  it("errors on string:replace with wrong types", () => {
    test:expect_error(() => string:replace("x", 9, "abc"))
    test:expect_error(() => string:replace("x", "y", 123))
  })
  it("errors on string:contains non-string", () => {
    test:expect_error(() => string:contains("a", 101))
    test:expect_error(() => string:contains(100, "abc"))
  })
  it("errors on string:starts_with/ends_with non-string", () => {
    test:expect_error(() => string:starts_with("a", 10))
    test:expect_error(() => string:ends_with(1, "foo"))
  })
  it("errors on string:slice with wrong types", () => {
    test:expect_error(() => string:slice([1,2,3], 1, 2))
    test:expect_error(() => string:slice("abc", "1", 2))
  })
  it("errors on string:repeat with non-integer", () => {
    test:expect_error(() => string:repeat("hi", "two"))
    test:expect_error(() => string:repeat([1,2,3], 2))
  })
  it("errors on string:index_of/last_index_of wrong types", () => {
    test:expect_error(() => string:index_of("a", 5))
    test:expect_error(() => string:last_index_of("a", []))
  })
})

