/**
 * @file test_max_transaction_time-t.cpp
 * @brief This test verifies that 'max_transaction_time' behaves properly.
 *
 * @details It verifies that connection with many transactions does not get
 * killed by the max_transaction_time implementation if each individual
 * transaction takes shorter than max_transaction_time.
 */

#include "mysql.h"

#include "proxysql_utils.h"
#include "tap.h"
#include "utils.h"

#include <unistd.h>
#include <iostream>

using std::string;

#include "json.hpp"

#define NUMQUERIES	15

using nlohmann::json;

using namespace std;

void parse_result_json_column(MYSQL_RES *result, json& j) {
	if(!result) return;
	MYSQL_ROW row;

	while ((row = mysql_fetch_row(result))) {
		j = json::parse(row[0]);
	}
}

// This test was previously failing due to replication not catching up quickly enough when doing
int main(int, char**) {
	CommandLine cl;

	plan(NUMQUERIES*2+1);

	if (cl.getEnv()) {
		diag("Failed to get the required environmental variables.");
		return EXIT_FAILURE;
	}

	MYSQL* admin = mysql_init(NULL);
	if (!mysql_real_connect(admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) {
		fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin));
		return EXIT_FAILURE;
	}

	diag("Configure ProxySQL to test mysql-max_transaction_time");
	MYSQL_QUERY_T(
		admin,
		"UPDATE global_variables SET variable_value = 4000 "
		"WHERE variable_name = 'mysql-max_transaction_time'"
	);
	MYSQL_QUERY_T(admin, "LOAD MYSQL VARIABLES TO RUNTIME");

	mysql_close(admin);

	MYSQL* proxy = mysql_init(NULL);
	if (!mysql_real_connect(proxy, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) {
		fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy));
		return EXIT_FAILURE;
	}

	diag("Run %d 1-second transactions:", NUMQUERIES);
	MYSQL_RES* myres;
	for (int i = 0; i < NUMQUERIES; i++) {
		MYSQL_QUERY_T(proxy, "BEGIN");

		MYSQL_QUERY_T(proxy, "SELECT SLEEP(1)");
		myres = mysql_store_result(proxy);
		if (myres == nullptr) {
			fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy));
			return EXIT_FAILURE;
		}
		mysql_free_result(myres);

		json j = {};
		MYSQL_QUERY_T(proxy, "PROXYSQL INTERNAL SESSION");
		myres = mysql_store_result(proxy);
		parse_result_json_column(myres, j);
		mysql_free_result(myres);
		int active_transactions = atoi(j["active_transactions"].dump().c_str());
		unsigned long long transaction_time_ms = atoll(j["transaction_time_ms"].dump().c_str());
		transaction_time_ms /= 1000;
		ok(active_transactions==1, "active_transactions = %d", active_transactions);
		ok(transaction_time_ms >= 900 && transaction_time_ms <= 1200, "Transaction time: %llu ms" , transaction_time_ms);
		MYSQL_QUERY_T(proxy, "COMMIT");
	}


	MYSQL_QUERY_T(proxy, "BEGIN");
	diag("Sleeping for 10 seconds so that the transaction times out");
	sleep(10);
	diag("Issuing PROXYSQL INTERNAL SESSION : it should fail");
	int query_err = mysql_query(proxy, "PROXYSQL INTERNAL SESSION");
	ok(query_err != 0 && mysql_errno(proxy) == 2013 , "Failed with error code %d : %s" , mysql_errno(proxy), mysql_error(proxy));
	if (query_err == 0) {
		json j = {};
		myres = mysql_store_result(proxy);
		parse_result_json_column(myres, j);
		mysql_free_result(myres);
		int active_transactions = atoi(j["active_transactions"].dump().c_str());
		unsigned long long transaction_time_ms = atoll(j["transaction_time_ms"].dump().c_str());
		transaction_time_ms /= 1000;
		diag("active_transactions = %d", active_transactions);
		diag("Transaction time: %llu ms" , transaction_time_ms);
	}
	mysql_close(proxy);

	return exit_status();
}
