local uri = require("uri")
local t = require("luatest")

local g = t.group("uri_integration")

local g_opts = t.group("uri_integration_opts", {
    { name = "RFC3986" },
    { name = "PATH" },
    { name = "PATH_PART" },
    { name = "QUERY" },
    { name = "QUERY_PART" },
    { name = "FRAGMENT" },
    { name = "FORM_URLENCODED" },
})

local escaped_bytes = {
    RFC3986 = {
        "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
        "%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F",
        "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
        "%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F",
        "%20", "%21", "%22", "%23", "%24", "%25", "%26", "%27",
        "%28", "%29", "%2A", "%2B", "%2C", "-", ".", "%2F", "0",
        "1", "2", "3", "4", "5", "6", "7", "8", "9", "%3A",
        "%3B", "%3C", "%3D", "%3E", "%3F", "%40", "A", "B", "C",
        "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
        "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y",
        "Z", "%5B", "%5C", "%5D", "%5E", "_", "%60", "a", "b",
        "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
        "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x",
        "y", "z", "%7B", "%7C", "%7D", "~", "%7F", "%80", "%81",
        "%82", "%83", "%84", "%85", "%86", "%87", "%88", "%89",
        "%8A", "%8B", "%8C", "%8D", "%8E", "%8F", "%90", "%91",
        "%92", "%93", "%94", "%95", "%96", "%97", "%98", "%99",
        "%9A", "%9B", "%9C", "%9D", "%9E", "%9F", "%A0", "%A1",
        "%A2", "%A3", "%A4", "%A5", "%A6", "%A7", "%A8", "%A9",
        "%AA", "%AB", "%AC", "%AD", "%AE", "%AF", "%B0", "%B1",
        "%B2", "%B3", "%B4", "%B5", "%B6", "%B7", "%B8", "%B9",
        "%BA", "%BB", "%BC", "%BD", "%BE", "%BF", "%C0", "%C1",
        "%C2", "%C3", "%C4", "%C5", "%C6", "%C7", "%C8", "%C9",
        "%CA", "%CB", "%CC", "%CD", "%CE", "%CF", "%D0", "%D1",
        "%D2", "%D3", "%D4", "%D5", "%D6", "%D7", "%D8", "%D9",
        "%DA", "%DB", "%DC", "%DD", "%DE", "%DF", "%E0", "%E1",
        "%E2", "%E3", "%E4", "%E5", "%E6", "%E7", "%E8", "%E9",
        "%EA", "%EB", "%EC", "%ED", "%EE", "%EF", "%F0", "%F1",
        "%F2", "%F3", "%F4", "%F5", "%F6", "%F7", "%F8", "%F9",
        "%FA", "%FB", "%FC", "%FD", "%FE", "%FF",
    },
    PATH = {
        "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
        "%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F",
        "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
        "%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F",
        "%20", "!", "%22", "%23", "$", "%25", "&", "'", "(", ")",
        "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4",
        "5", "6", "7", "8", "9", ":", ";", "%3C", "=", "%3E",
        "%3F", "@", "A", "B", "C", "D", "E", "F", "G", "H", "I",
        "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
        "U", "V", "W", "X", "Y", "Z", "%5B", "%5C", "%5D", "%5E",
        "_", "%60", "a", "b", "c", "d", "e", "f", "g", "h", "i",
        "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
        "u", "v", "w", "x", "y", "z", "%7B", "%7C", "%7D", "~",
        "%7F", "%80", "%81", "%82", "%83", "%84", "%85", "%86",
        "%87", "%88", "%89", "%8A", "%8B", "%8C", "%8D", "%8E",
        "%8F", "%90", "%91", "%92", "%93", "%94", "%95", "%96",
        "%97", "%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E",
        "%9F", "%A0", "%A1", "%A2", "%A3", "%A4", "%A5", "%A6",
        "%A7", "%A8", "%A9", "%AA", "%AB", "%AC", "%AD", "%AE",
        "%AF", "%B0", "%B1", "%B2", "%B3", "%B4", "%B5", "%B6",
        "%B7", "%B8", "%B9", "%BA", "%BB", "%BC", "%BD", "%BE",
        "%BF", "%C0", "%C1", "%C2", "%C3", "%C4", "%C5", "%C6",
        "%C7", "%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE",
        "%CF", "%D0", "%D1", "%D2", "%D3", "%D4", "%D5", "%D6",
        "%D7", "%D8", "%D9", "%DA", "%DB", "%DC", "%DD", "%DE",
        "%DF", "%E0", "%E1", "%E2", "%E3", "%E4", "%E5", "%E6",
        "%E7", "%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE",
        "%EF", "%F0", "%F1", "%F2", "%F3", "%F4", "%F5", "%F6",
        "%F7", "%F8", "%F9", "%FA", "%FB", "%FC", "%FD", "%FE",
        "%FF",
    },
    PATH_PART = {
        "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
        "%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F",
        "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
        "%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F",
        "%20", "!", "%22", "%23", "$", "%25", "&", "'", "(", ")",
        "*", "+", ",", "-", ".", "%2F", "0", "1", "2", "3",
        "4", "5", "6", "7", "8", "9", ":", ";", "%3C", "=",
        "%3E", "%3F", "@", "A", "B", "C", "D", "E", "F", "G",
        "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
        "S", "T", "U", "V", "W", "X", "Y", "Z", "%5B", "%5C",
        "%5D", "%5E", "_", "%60", "a", "b", "c", "d", "e", "f",
        "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q",
        "r", "s", "t", "u", "v", "w", "x", "y", "z", "%7B",
        "%7C", "%7D", "~", "%7F", "%80", "%81", "%82", "%83",
        "%84", "%85", "%86", "%87", "%88", "%89", "%8A", "%8B",
        "%8C", "%8D", "%8E", "%8F", "%90", "%91", "%92", "%93",
        "%94", "%95", "%96", "%97", "%98", "%99", "%9A", "%9B",
        "%9C", "%9D", "%9E", "%9F", "%A0", "%A1", "%A2", "%A3",
        "%A4", "%A5", "%A6", "%A7", "%A8", "%A9", "%AA", "%AB",
        "%AC", "%AD", "%AE", "%AF", "%B0", "%B1", "%B2", "%B3",
        "%B4", "%B5", "%B6", "%B7", "%B8", "%B9", "%BA", "%BB",
        "%BC", "%BD", "%BE", "%BF", "%C0", "%C1", "%C2", "%C3",
        "%C4", "%C5", "%C6", "%C7", "%C8", "%C9", "%CA", "%CB",
        "%CC", "%CD", "%CE", "%CF", "%D0", "%D1", "%D2", "%D3",
        "%D4", "%D5", "%D6", "%D7", "%D8", "%D9", "%DA", "%DB",
        "%DC", "%DD", "%DE", "%DF", "%E0", "%E1", "%E2", "%E3",
        "%E4", "%E5", "%E6", "%E7", "%E8", "%E9", "%EA", "%EB",
        "%EC", "%ED", "%EE", "%EF", "%F0", "%F1", "%F2", "%F3",
        "%F4", "%F5", "%F6", "%F7", "%F8", "%F9", "%FA", "%FB",
        "%FC", "%FD", "%FE", "%FF",
    },
    QUERY = {
        "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
        "%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F",
        "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
        "%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F",
        "%20", "!", "%22", "%23", "$", "%25", "&", "'", "(", ")",
        "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4",
        "5", "6", "7", "8", "9", "%3A", ";", "%3C", "=", "%3E",
        "?", "%40", "A", "B", "C", "D", "E", "F", "G", "H",
        "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S",
        "T", "U", "V", "W", "X", "Y", "Z", "%5B", "%5C", "%5D",
        "%5E", "_", "%60", "a", "b", "c", "d", "e", "f", "g",
        "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
        "s", "t", "u", "v", "w", "x", "y", "z", "%7B", "%7C",
        "%7D", "~", "%7F", "%80", "%81", "%82", "%83", "%84",
        "%85", "%86", "%87", "%88", "%89", "%8A", "%8B", "%8C",
        "%8D", "%8E", "%8F", "%90", "%91", "%92", "%93", "%94",
        "%95", "%96", "%97", "%98", "%99", "%9A", "%9B", "%9C",
        "%9D", "%9E", "%9F", "%A0", "%A1", "%A2", "%A3", "%A4",
        "%A5", "%A6", "%A7", "%A8", "%A9", "%AA", "%AB", "%AC",
        "%AD", "%AE", "%AF", "%B0", "%B1", "%B2", "%B3", "%B4",
        "%B5", "%B6", "%B7", "%B8", "%B9", "%BA", "%BB", "%BC",
        "%BD", "%BE", "%BF", "%C0", "%C1", "%C2", "%C3", "%C4",
        "%C5", "%C6", "%C7", "%C8", "%C9", "%CA", "%CB", "%CC",
        "%CD", "%CE", "%CF", "%D0", "%D1", "%D2", "%D3", "%D4",
        "%D5", "%D6", "%D7", "%D8", "%D9", "%DA", "%DB", "%DC",
        "%DD", "%DE", "%DF", "%E0", "%E1", "%E2", "%E3", "%E4",
        "%E5", "%E6", "%E7", "%E8", "%E9", "%EA", "%EB", "%EC",
        "%ED", "%EE", "%EF", "%F0", "%F1", "%F2", "%F3", "%F4",
        "%F5", "%F6", "%F7", "%F8", "%F9", "%FA", "%FB", "%FC",
        "%FD", "%FE", "%FF",
    },
    QUERY_PART = {
        "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
        "%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F",
        "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
        "%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F",
        "%20", "!", "%22", "%23", "$", "%25", "%26", "'", "(", ")",
        "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4",
        "5", "6", "7", "8", "9", "%3A", ";", "%3C", "%3D", "%3E",
        "?", "%40", "A", "B", "C", "D", "E", "F", "G", "H", "I",
        "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
        "U", "V", "W", "X", "Y", "Z", "%5B", "%5C", "%5D", "%5E",
        "_", "%60", "a", "b", "c", "d", "e", "f", "g", "h", "i",
        "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
        "u", "v", "w", "x", "y", "z", "%7B", "%7C", "%7D", "~",
        "%7F", "%80", "%81", "%82", "%83", "%84", "%85", "%86",
        "%87", "%88", "%89", "%8A", "%8B", "%8C", "%8D", "%8E",
        "%8F", "%90", "%91", "%92", "%93", "%94", "%95", "%96",
        "%97", "%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E",
        "%9F", "%A0", "%A1", "%A2", "%A3", "%A4", "%A5", "%A6",
        "%A7", "%A8", "%A9", "%AA", "%AB", "%AC", "%AD", "%AE",
        "%AF", "%B0", "%B1", "%B2", "%B3", "%B4", "%B5", "%B6",
        "%B7", "%B8", "%B9", "%BA", "%BB", "%BC", "%BD", "%BE",
        "%BF", "%C0", "%C1", "%C2", "%C3", "%C4", "%C5", "%C6",
        "%C7", "%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE",
        "%CF", "%D0", "%D1", "%D2", "%D3", "%D4", "%D5", "%D6",
        "%D7", "%D8", "%D9", "%DA", "%DB", "%DC", "%DD", "%DE",
        "%DF", "%E0", "%E1", "%E2", "%E3", "%E4", "%E5", "%E6",
        "%E7", "%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE",
        "%EF", "%F0", "%F1", "%F2", "%F3", "%F4", "%F5", "%F6",
        "%F7", "%F8", "%F9", "%FA", "%FB", "%FC", "%FD", "%FE",
        "%FF",
    },
    FRAGMENT = {
        "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
        "%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F",
        "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
        "%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F",
        "%20", "!", "%22", "%23", "$", "%25", "&", "'", "(", ")",
        "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4",
        "5", "6", "7", "8", "9", "%3A", ";", "%3C", "=", "%3E",
        "?", "%40", "A", "B", "C", "D", "E", "F", "G", "H",
        "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S",
        "T", "U", "V", "W", "X", "Y", "Z", "%5B", "%5C", "%5D",
        "%5E", "_", "%60", "a", "b", "c", "d", "e", "f", "g",
        "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
        "s", "t", "u", "v", "w", "x", "y", "z", "%7B", "%7C",
        "%7D", "~", "%7F", "%80", "%81", "%82", "%83", "%84",
        "%85", "%86", "%87", "%88", "%89", "%8A", "%8B", "%8C",
        "%8D", "%8E", "%8F", "%90", "%91", "%92", "%93", "%94",
        "%95", "%96", "%97", "%98", "%99", "%9A", "%9B", "%9C",
        "%9D", "%9E", "%9F", "%A0", "%A1", "%A2", "%A3", "%A4",
        "%A5", "%A6", "%A7", "%A8", "%A9", "%AA", "%AB", "%AC",
        "%AD", "%AE", "%AF", "%B0", "%B1", "%B2", "%B3", "%B4",
        "%B5", "%B6", "%B7", "%B8", "%B9", "%BA", "%BB", "%BC",
        "%BD", "%BE", "%BF", "%C0", "%C1", "%C2", "%C3", "%C4",
        "%C5", "%C6", "%C7", "%C8", "%C9", "%CA", "%CB", "%CC",
        "%CD", "%CE", "%CF", "%D0", "%D1", "%D2", "%D3", "%D4",
        "%D5", "%D6", "%D7", "%D8", "%D9", "%DA", "%DB", "%DC",
        "%DD", "%DE", "%DF", "%E0", "%E1", "%E2", "%E3", "%E4",
        "%E5", "%E6", "%E7", "%E8", "%E9", "%EA", "%EB", "%EC",
        "%ED", "%EE", "%EF", "%F0", "%F1", "%F2", "%F3", "%F4",
        "%F5", "%F6", "%F7", "%F8", "%F9", "%FA", "%FB", "%FC",
        "%FD", "%FE", "%FF",
    },
    FORM_URLENCODED = {
        "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
        "%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F",
        "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
        "%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F",
        "+", "%21", "%22", "%23", "%24", "%25", "%26", "%27",
        "%28", "%29", "*", "%2B", "%2C", "-", ".", "%2F", "0",
        "1", "2", "3", "4", "5", "6", "7", "8", "9", "%3A",
        "%3B", "%3C", "%3D", "%3E", "%3F", "%40", "A", "B",
        "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
        "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X",
        "Y", "Z", "%5B", "%5C", "%5D", "%5E", "_", "%60", "a",
        "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l",
        "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w",
        "x", "y", "z", "%7B", "%7C", "%7D", "%7E", "%7F", "%80",
        "%81", "%82", "%83", "%84", "%85", "%86", "%87", "%88",
        "%89", "%8A", "%8B", "%8C", "%8D", "%8E", "%8F", "%90",
        "%91", "%92", "%93", "%94", "%95", "%96", "%97", "%98",
        "%99", "%9A", "%9B", "%9C", "%9D", "%9E", "%9F", "%A0",
        "%A1", "%A2", "%A3", "%A4", "%A5", "%A6", "%A7", "%A8",
        "%A9", "%AA", "%AB", "%AC", "%AD", "%AE", "%AF", "%B0",
        "%B1", "%B2", "%B3", "%B4", "%B5", "%B6", "%B7", "%B8",
        "%B9", "%BA", "%BB", "%BC", "%BD", "%BE", "%BF", "%C0",
        "%C1", "%C2", "%C3", "%C4", "%C5", "%C6", "%C7", "%C8",
        "%C9", "%CA", "%CB", "%CC", "%CD", "%CE", "%CF", "%D0",
        "%D1", "%D2", "%D3", "%D4", "%D5", "%D6", "%D7", "%D8",
        "%D9", "%DA", "%DB", "%DC", "%DD", "%DE", "%DF", "%E0",
        "%E1", "%E2", "%E3", "%E4", "%E5", "%E6", "%E7", "%E8",
        "%E9", "%EA", "%EB", "%EC", "%ED", "%EE", "%EF", "%F0",
        "%F1", "%F2", "%F3", "%F4", "%F5", "%F6", "%F7", "%F8",
        "%F9", "%FA", "%FB", "%FC", "%FD", "%FE", "%FF",
    },
}

local build_extended_ascii_str = function()
    local str = ""
    for c = 0, 255 do
        str = str .. string.char(c)
    end
    return str
end

g_opts.test_uri_escape = function(cg)
    local opts_name = cg.params.name
    local opts = uri[opts_name]
    -- Traverse an Extended ASCII table.
    for c = 0, 255 do
        local ch = string.char(c)
        local expected = escaped_bytes[opts_name][c + 1]
        t.assert_equals(uri.escape(ch, opts), expected)
    end
end

g_opts.test_uri_unescape = function(cg)
    local opts_name = cg.params.name
    local opts = uri[opts_name]
    -- Traverse an Extended ASCII table.
    for c = 0, 255 do
        local ch = string.char(c)
        local actual = uri.unescape(escaped_bytes[opts_name][c + 1], opts)
        t.assert_equals(actual, ch)
    end
end

g.test_uri_unescape_icase= function(_)
    local escaped_bytes_lowercase = "%1a%1b%1c%1d%1e%1f"
    local escaped_bytes_uppercase = "%1A%1B%1C%1D%1E%1F"
    local unescaped_lowercase = uri.unescape(escaped_bytes_lowercase)
    local unescaped_uppercase = uri.unescape(escaped_bytes_uppercase)
    t.assert_equals("\26\27\28\29\30\31", unescaped_lowercase)
    t.assert_equals(unescaped_uppercase, unescaped_lowercase)
end

g.test_uri_unreserved_type = function(_)
    t.assert_not_equals(uri.unreserved, nil)
    t.assert_type(uri.unreserved, "function")
    t.assert_type(uri.unreserved("string"), "cdata")
end

g.test_uri_unreserved = function(_)
    local opts_pattern = {
        unreserved = uri.unreserved("a-z"),
    }
    local opts_string = {
        unreserved = uri.unreserved("abcdefghijklmnopqrstuvwxyz"),
    }
    local str = "abcdefghijklmnopqrstuvwxyzВК"
    t.assert_equals(uri.escape(str, opts_string),
                    "abcdefghijklmnopqrstuvwxyz%D0%92%D0%9A")
    t.assert_equals(uri.escape(str, opts_pattern),
                    "abcdefghijklmnopqrstuvwxyz%D0%92%D0%9A")
end

g_opts.test_uri_opts = function(cg)
    local opts = uri[cg.params.name]
    t.assert_not_equals(opts, nil)
    t.assert_type(opts, "table")
    t.assert_not_equals(opts.unreserved, nil)
    t.assert_not_equals(opts.plus, nil)
end

g.test_uri_escape_RFC3986_is_default = function(_)
    local str = build_extended_ascii_str()
    local escaped_default_opts = uri.escape(str)
    local escaped_RFC3986_opts = uri.escape(str, uri.RFC3986)
    t.assert_equals(escaped_default_opts, escaped_RFC3986_opts)
end

g.test_uri_unescape_RFC3986_is_default = function(_)
    local str = build_extended_ascii_str()
    local escaped_RFC3986_opts = uri.escape(str, uri.RFC3986) .. "+"
    local unescaped_RFC3986_opts = uri.unescape(escaped_RFC3986_opts,
                                                uri.RFC3986)
    local unescaped_default_opts = uri.unescape(escaped_RFC3986_opts)
    t.assert_equals(unescaped_RFC3986_opts, unescaped_default_opts)
end
