#include "test.h"
#include <regex>

#define TEST(name) TEST_CASE("query: " name, "[query]")

#define CHECKQSTR(uri, re) CHECK(std::regex_search(uri.query_string().c_str(), std::regex("(^|&)" re "(&|$)")));

using vs = std::vector<string>;

template <class T>
static vs getvs (T&& range) {
    vs ret;
    for (auto it = range.first; it != range.second; ++it) ret.push_back(it->second);
    return ret;
}

TEST("query hash") {
    URI uri("https://ya.ru/my/path?p1=v1&p2=v2&p3=a%20b&p2=v2v2&=empty&empty=&#myhash");
    CHECK(uri.query() == Query({
        {"p1", "v1"},
        {"p2", "v2"},
        {"p3", "a b"},
        {"p2", "v2v2"},
        {"", "empty"},
        {"empty", ""},
        {"", ""}
    }));
}

TEST("query hash set") {
    URI uri("https://ya.ru?p1=v1&empty=");
    uri.query({{"a", "1"}, {"key space", "2"}, {"b", "val space"}, {"multi", "1"}, {"multi", "2"}, {"multi", "3"}, {"", "empty"}});

    auto qstr = uri.query_string();
    CHECK(qstr);
    CHECKQSTR(uri, "a=1");
    CHECKQSTR(uri, "key%20space=2");
    CHECKQSTR(uri, "b=val%20space");
    CHECKQSTR(uri, "multi=1");
    CHECKQSTR(uri, "multi=2");
    CHECKQSTR(uri, "multi=3");
    CHECKQSTR(uri, "=empty");
}

TEST("param") {
    URI uri("https://ya.ru/my/path?p1=v1&p2=v2&p3=a%20b&p2=v2v2&=empty&empty=&#myhash");
    CHECK(uri.query().size() == 7);
    CHECK(uri.param("p1") == "v1");
    CHECK(uri.param("p3") == "a b");

    CHECK(uri.has_param(""));
    CHECK(uri.param("empty") == "");

    CHECK(!uri.has_param("nonexistent"));

    CHECK(uri.param("p2") == "v2");
}

TEST("set param") {
    URI uri("https://ya.ru/?p1=v1&p2=v2&p2=v2v2");

    uri.param("p1", "hi");
    CHECK(getvs(uri.multiparam("p1")) == vs{"hi"});

    uri.param("p2", "hello");
    CHECK(getvs(uri.multiparam("p2")) == vs{"hello"});

    uri.param("p3", "world");
    CHECK(getvs(uri.multiparam("p3")) == vs{"world"});
}

TEST("remove param") {
    URI uri("https://ya.ru/?p1=v1&p2=v2&p2=v2v2");
    uri.remove_param("nonexistent");
    CHECK(uri.query() == Query({{"p1", "v1"}, {"p2", "v2"}, {"p2", "v2v2"}}));
    uri.remove_param("p2");
    CHECK(uri.query() == Query({{"p1", "v1"}}));
    uri.remove_param("p1");
    CHECK(uri.query() == Query());
}

TEST("multiparam") {
    URI uri("https://ya.ru/my/path?p1=v1&p2=v2&p3=a%20b&p2=v2v2&=empty&empty=&#myhash");
    CHECK(getvs(uri.multiparam("p1")) == vs{"v1"});
    CHECK(getvs(uri.multiparam("")) == vs{"empty", ""});
    CHECK(getvs(uri.multiparam("p2")) == vs{"v2", "v2v2"});
}

TEST("set multiparam") {
    URI uri("https://ya.ru/?p1=v1&p2=v2&p2=v2v2");

    uri.multiparam("p1", {"1", "2"});
    CHECK(getvs(uri.multiparam("p1")) == vs{"1", "2"});

    uri.multiparam("p2", {"a"});
    CHECK(getvs(uri.multiparam("p2")) == vs{"a"});

    uri.multiparam("p1", {});
    CHECK(!uri.has_param("p1"));

    uri.multiparam("p3", {"a", "b", "c"});
    CHECK(getvs(uri.multiparam("p3")) == vs{"a", "b", "c"});
}

TEST("query string") {
    URI uri("https://ya.ru/?p1=v1&p2=v2&p2=v2%20v2");
    CHECK(uri.query_string() == "p1=v1&p2=v2&p2=v2%20v2");
    uri.multiparam("p1", {});
    CHECK(uri.query_string() == "p2=v2&p2=v2%20v2");
}

TEST("set query string") {
    URI uri("https://ya.ru/?p1=v1&p2=v2&p2=v2%20v2");
    uri.query_string("a=1&b=2");
    CHECK(uri.query() == Query({{"a", "1"}, {"b", "2"}}));
}

TEST("add query string") {
    URI uri("https://ya.ru/my/path?a=b");
    uri.add_query("");
    CHECK(uri.to_string() == "https://ya.ru/my/path?a=b");

    uri.add_query("a=2");
    CHECK(uri.query() == Query({{"a", "b"}, {"a", "2"}}));

    uri.add_query("c=d&e=f%20e");
    CHECK(uri.query() == Query({{"a", "b"}, {"a", "2"}, {"c", "d"}, {"e", "f e"}}));
}

TEST("add query hash") {
    URI uri("https://ya.ru/my/path?a=b&e=f");
    uri.add_query({{"c", "d"}, {"e", "f e"}});
    CHECK(uri.query() == Query({{"a", "b"}, {"c", "d"}, {"e", "f"}, {"e", "f e"}}));
}

TEST("query in ctor is added") {
    URI uri("https://ya.ru/my/path?a=b&c=d", {{"c", "d e"}, {"e", "f"}});
    CHECK(uri.query().size() == 4);
    CHECK(uri.query() == Query({{"a", "b"}, {"c", "d"}, {"c", "d e"}, {"e", "f"}}));
    CHECKQSTR(uri, "a=b");
    CHECKQSTR(uri, "c=d");
    CHECKQSTR(uri, "c=d%20e");
    CHECKQSTR(uri, "e=f");
}

TEST("QUERY_PARAM_SEMICOLON") {
    URI uri("https://ya.ru/my/path?a=b;e=f;c=d%20e", URI::Flags::query_param_semicolon);
    CHECK(uri.query() == Query({{"a", "b"}, {"c", "d e"}, {"e", "f"}}));
    CHECK(std::regex_search(uri.query_string().c_str(), std::regex("[^;]+;[^;]+;[^;]+")));
}

TEST("bug test (no sync query for param())") {
    URI uri("https://graph.facebook.com/v2.2?fields=id%2Cfirst_name%2Clast_name%2Cname%2Cgender%2Cbirthday%2Clink&ids=me&include_headers=false");
    uri.query_string("");
    uri.param("batch", "123");
    CHECK(uri.to_string() == "https://graph.facebook.com/v2.2?batch=123");

}