namespace TweetTest.Core.StringUtils

open Xunit
open TweetDuck.Core.Utils


module ExtractBefore =

    [<Fact>]
    let ``empty input string returns empty string`` () =
        Assert.Equal("", StringUtils.ExtractBefore("", ' '))

    [<Fact>]
    let ``input string w/o searched char returns input string`` () =
        Assert.Equal("abc", StringUtils.ExtractBefore("abc", ' '))

    [<Fact>]
    let ``finding searched char returns everything before it`` () =
        Assert.Equal("abc", StringUtils.ExtractBefore("abc def ghi", ' '))

    [<Theory>]
    [<InlineData(0, "abc")>]
    [<InlineData(3, "abc")>]
    [<InlineData(4, "abc def")>]
    [<InlineData(7, "abc def")>]
    [<InlineData(8, "abc def ghi")>]
    let ``start index works`` (startIndex: int, expectedValue: string) =
        Assert.Equal(expectedValue, StringUtils.ExtractBefore("abc def ghi", ' ', startIndex))
        

module ParseInts =
    open System
    
    [<Fact>]
    let ``empty input string returns empty collection`` () =
        Assert.Empty(StringUtils.ParseInts("", ','))
    
    [<Fact>]
    let ``single integer is parsed correctly`` () =
        Assert.Equal([ 1 ], StringUtils.ParseInts("1", ','))
    
    [<Fact>]
    let ``multiple integers are parsed correctly`` () =
        Assert.Equal([ -3 .. 3 ], StringUtils.ParseInts("-3,-2,-1,0,1,2,3", ','))
    
    [<Fact>]
    let ``excessive delimiters are discarded`` () =
        Assert.Equal([ 1 .. 4 ], StringUtils.ParseInts(",,1,,,2,3,,4,,,", ','))

    [<Fact>]
    let ``invalid format throws exception`` () =
        Assert.Throws<FormatException>(fun () -> StringUtils.ParseInts("1,2,a", ',') |> ignore)


module ConvertPascalCaseToScreamingSnakeCase =

    [<Fact>]
    let ``converts one word`` () =
        Assert.Equal("HELP", StringUtils.ConvertPascalCaseToScreamingSnakeCase("Help"))

    [<Fact>]
    let ``converts two words`` () =
        Assert.Equal("HELP_ME", StringUtils.ConvertPascalCaseToScreamingSnakeCase("HelpMe"))

    [<Fact>]
    let ``converts many words`` () =
        Assert.Equal("HELP_ME_PLEASE", StringUtils.ConvertPascalCaseToScreamingSnakeCase("HelpMePlease"))

    [<Fact>]
    let ``converts one uppercase abbreviation`` () =
        Assert.Equal("HTML_CODE", StringUtils.ConvertPascalCaseToScreamingSnakeCase("HTMLCode"))

    [<Fact>]
    let ``converts many uppercase abbreviations`` () =
        Assert.Equal("I_LIKE_HTML_AND_CSS", StringUtils.ConvertPascalCaseToScreamingSnakeCase("ILikeHTMLAndCSS"))


module ConvertRot13 =
        
    [<Fact>]
    let ``ignores digits and special characters`` () =
        Assert.Equal("<123'456.789>", StringUtils.ConvertRot13("<123'456.789>"))

    [<Fact>]
    let ``converts lowercase letters correctly`` () =
        Assert.Equal("nopqrstuvwxyzabcdefghijklm", StringUtils.ConvertRot13("abcdefghijklmnopqrstuvwxyz"))
        
    [<Fact>]
    let ``converts uppercase letters correctly`` () =
        Assert.Equal("NOPQRSTUVWXYZABCDEFGHIJKLM", StringUtils.ConvertRot13("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
        
    [<Fact>]
    let ``converts mixed character types correctly`` () =
        Assert.Equal("Uryyb, jbeyq! :)", StringUtils.ConvertRot13("Hello, world! :)"))


module CountOccurrences =
    open System

    [<Fact>]
    let ``empty input string returns zero`` () =
        Assert.Equal(0, StringUtils.CountOccurrences("", "a"))

    [<Fact>]
    let ``empty searched string throws`` () =
        Assert.Throws<ArgumentOutOfRangeException>(fun () -> StringUtils.CountOccurrences("hello", "") |> ignore)

    [<Fact>]
    let ``counts single letter characters correctly`` () =
        Assert.Equal(3, StringUtils.CountOccurrences("hello world", "l"))

    [<Fact>]
    let ``counts longer substrings correctly`` () =
        Assert.Equal(2, StringUtils.CountOccurrences("hello and welcome in hell", "hell"))

    [<Fact>]
    let ``does not count overlapping substrings`` () =
        Assert.Equal(2, StringUtils.CountOccurrences("aaaaa", "aa"))