#include "msgpack_cproto_api.h"

#include <unordered_set>
#include "gtests/tests/tests_data.h"
#include "query_aggregate_strict_mode_test.h"

TEST_F(MsgPackCprotoApi, MsgPackDecodeTest) {
	using namespace reindexer;
	auto testDataPath = reindexer::fs::JoinPath(std::string(kTestsDataPath), "MsgPack");
	auto msgPackPath = fs::JoinPath(testDataPath, "msg.uu");
	auto msgJsonPath = fs::JoinPath(testDataPath, "msg.json");

	std::string content;
	int res = reindexer::fs::ReadFile(msgPackPath, content);
	ASSERT_GT(res, 0) << "Test data file not found: '" << msgPackPath << "'";
	reindexer::client::Item msgPackItem = client_->NewItem(default_namespace);
	if (res > 0) {
		size_t offset = 0;
		auto err = msgPackItem.FromMsgPack(content, offset);
		ASSERT_TRUE(err.ok()) << err.what();
	}

	content.clear();
	res = reindexer::fs::ReadFile(msgJsonPath, content);
	ASSERT_GT(res, 0) << "Test data file not found: '" << msgJsonPath << "'";
	ASSERT_GT(content.size(), 1);

	reindexer::client::Item msgJsonItem = client_->NewItem(default_namespace);
	if (res > 0) {
		auto err = msgJsonItem.FromJSON(content);
		ASSERT_TRUE(err.ok()) << err.what();
	}
	EXPECT_EQ(msgJsonItem.GetJSON(), msgPackItem.GetJSON());
}

TEST_F(MsgPackCprotoApi, SelectTest) {
	reindexer::client::QueryResults qr;
	Error err = client_->Select(Query(default_namespace), qr, ctx_, nullptr, FormatMsgPack);
	ASSERT_TRUE(err.ok()) << err.what();
	EXPECT_EQ(qr.Count(), 1000);
	for (auto it : qr) {
		checkItem(it);
	}
}

TEST_F(MsgPackCprotoApi, AggregationSelectTest) {
	reindexer::client::QueryResults qr;
	Error err =
		client_->Select("select distinct(id), facet(a1, a2), sum(id) from test_namespace limit 100000", qr, ctx_, nullptr, FormatMsgPack);
	ASSERT_TRUE(err.ok()) << err.what();
	ASSERT_EQ(qr.GetAggregationResults().size(), 3);

	const reindexer::AggregationResult& distinct = qr.GetAggregationResults()[0];
	EXPECT_EQ(distinct.type, AggDistinct);
	EXPECT_EQ(distinct.distincts.size(), 1000);
	ASSERT_EQ(distinct.fields.size(), 1);
	EXPECT_EQ(distinct.fields[0], kFieldId);
	std::unordered_set<int> found;
	for (size_t i = 0; i < distinct.distincts.size(); ++i) {
		found.insert(reindexer::stoi(distinct.distincts[i].As<std::string>(distinct.payloadType, distinct.distinctsFields)));
	}
	ASSERT_EQ(distinct.distincts.size(), found.size());

	for (size_t i = 0; i < distinct.distincts.size(); ++i) {
		EXPECT_NE(found.find(i), found.end());
	}

	const reindexer::AggregationResult& facet = qr.GetAggregationResults()[1];
	EXPECT_EQ(facet.type, AggFacet);
	EXPECT_EQ(facet.facets.size(), 1000);
	ASSERT_EQ(facet.fields.size(), 2);
	EXPECT_EQ(facet.fields[0], kFieldA1);
	EXPECT_EQ(facet.fields[1], kFieldA2);

	for (const reindexer::FacetResult& res : facet.facets) {
		EXPECT_EQ(res.count, 1);
		const auto v1 = reindexer::stoll(res.values[0]);
		const auto v2 = reindexer::stoll(res.values[1]);
		EXPECT_EQ(v1 * 3, v2 * 2);
	}

	const reindexer::AggregationResult& sum = qr.GetAggregationResults()[2];
	EXPECT_EQ(sum.type, AggSum);
	ASSERT_EQ(sum.fields.size(), 1);
	EXPECT_EQ(sum.fields[0], kFieldId);
	double val = (999.0 / 2.0) * 1000.0;
	EXPECT_DOUBLE_EQ(sum.GetValueOrZero(), val) << sum.GetValueOrZero() << "; " << val;
}

TEST_F(MsgPackCprotoApi, AggregationsWithStrictModeTest) { QueryAggStrictModeTest(client_); }

TEST_F(MsgPackCprotoApi, ModifyItemsTest) {
	reindexer::client::Item item = client_->NewItem(default_namespace);
	ASSERT_TRUE(item.Status().ok()) << item.Status().what();

	reindexer::WrSerializer wrser;
	reindexer::JsonBuilder jsonBuilder(wrser, reindexer::ObjType::TypeObject);
	jsonBuilder.Put(kFieldId, 7);
	jsonBuilder.Put(kFieldA1, 77);
	jsonBuilder.Put(kFieldA2, 777);
	jsonBuilder.Put(kFieldA3, 7777);
	jsonBuilder.End();

	std::string itemSrcJson(wrser.Slice());

	char* endp = nullptr;
	Error err = item.FromJSON(wrser.Slice(), &endp);
	ASSERT_TRUE(err.ok()) << err.what();

	err = client_->Upsert(default_namespace, item, ctx_, FormatMsgPack);
	ASSERT_TRUE(err.ok()) << err.what();

	reindexer::client::QueryResults qr;
	err = client_->Select(Query(default_namespace).Where(kFieldId, CondEq, Variant(int(7))), qr, ctx_, nullptr, FormatMsgPack);
	ASSERT_TRUE(err.ok()) << err.what();
	ASSERT_TRUE(qr.Count() == 1);

	for (auto it : qr) {
		checkItem(it);
		reindexer::client::Item item = it.GetItem();
		ASSERT_TRUE(itemSrcJson == std::string(item.GetJSON()));
	}
}

TEST_F(MsgPackCprotoApi, UpdateTest) {
	const std::string_view sql = "update test_namespace set a1 = 7 where id >= 10 and id <= 100";
	Query q = Query::FromSQL(sql);

	reindexer::client::QueryResults qr;
	Error err = client_->Update(q, qr, ctx_, FormatMsgPack);
	ASSERT_TRUE(err.ok()) << err.what();
	ASSERT_TRUE(qr.Count() == 91);

	int id = 10;
	for (auto it : qr) {
		checkItem(it);

		reindexer::WrSerializer json;
		reindexer::JsonBuilder jsonBuilder(json, reindexer::ObjType::TypeObject);
		jsonBuilder.Put(kFieldId, id);
		jsonBuilder.Put(kFieldA1, 7);
		jsonBuilder.Put(kFieldA2, id * 3);
		jsonBuilder.Put(kFieldA3, id * 4);
		jsonBuilder.End();

		reindexer::client::Item item = it.GetItem();
		ASSERT_TRUE(item.GetJSON() == json.Slice());

		++id;
	}
}

TEST_F(MsgPackCprotoApi, DeleteTest) {
	const std::string_view sql = "delete from test_namespace where id >= 100 and id <= 110";
	Query q = Query::FromSQL(sql);

	reindexer::client::QueryResults qr;
	Error err = client_->Delete(q, qr, ctx_, FormatMsgPack);
	ASSERT_TRUE(err.ok()) << err.what();
	ASSERT_TRUE(qr.Count() == 11);

	int id = 100;
	for (auto it : qr) {
		checkItem(it);

		reindexer::WrSerializer json;
		reindexer::JsonBuilder jsonBuilder(json, reindexer::ObjType::TypeObject);
		jsonBuilder.Put(kFieldId, id);
		jsonBuilder.Put(kFieldA1, id * 2);
		jsonBuilder.Put(kFieldA2, id * 3);
		jsonBuilder.Put(kFieldA3, id * 4);
		jsonBuilder.End();

		reindexer::client::Item item = it.GetItem();
		ASSERT_TRUE(item.GetJSON() == json.Slice());

		++id;
	}
}
