#include "catch.hpp"
#include "test_helpers.hpp"
#include "duckdb/main/config.hpp"
#include "test_config.hpp"

using namespace duckdb;
using namespace std;

void rand_str(char *dest, idx_t length) {
	char charset[] = "0123456789"
	                 "abcdefghijklmnopqrstuvwxyz"
	                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

	while (length-- > 0) {
		idx_t index = (double)rand() / RAND_MAX * (sizeof charset - 1);
		*dest++ = charset[index];
	}
	*dest = '\0';
}

TEST_CASE("Test repeated appending small chunks to a table", "[memoryleak]") {
	if (!TestConfiguration::TestMemoryLeaks()) {
		return;
	}
	duckdb_database db;
	duckdb_connection con;
	duckdb_state state;
	auto db_path = TestCreatePath("appender_leak_test.db");
	TestDeleteFile(db_path);

	if (duckdb_open(db_path.c_str(), &db) == DuckDBError) {
		// handle error
		FAIL("Failed to open");
	}
	if (duckdb_connect(db, &con) == DuckDBError) {
		// handle error
		FAIL("Failed to connect");
	}

	state =
	    duckdb_query(con, "create table test(col1 varchar, col2 varchar, col3 bigint, col4 bigint, col5 double)", NULL);
	if (state == DuckDBError) {
		FAIL("Failed to create table");
	}
	state = duckdb_query(con, "set memory_limit='100mb'", NULL);
	if (state == DuckDBError) {
		FAIL("Failed to set memory limit");
	}

	long n1 = 0;
	double d1 = 0.5;
	for (int i = 0; i < 100000; i++) {
		duckdb_appender appender;
		if (duckdb_appender_create(con, NULL, "test", &appender) == DuckDBError) {
			FAIL("Failed to create appender");
		}
		for (int j = 0; j < 1000; j++) {
			char str[41];
			rand_str(str, sizeof(str) - 1);
			duckdb_append_varchar(appender, str);
			duckdb_append_varchar(appender, "hello");
			duckdb_append_int64(appender, n1++);
			duckdb_append_int64(appender, n1++);
			duckdb_append_double(appender, d1);
			d1 += 1.25;
			duckdb_appender_end_row(appender);
		}
		state = duckdb_appender_close(appender);
		if (state == DuckDBError) {
			FAIL("Failed to close appender");
		}
		state = duckdb_appender_destroy(&appender);
		if (state == DuckDBError) {
			fprintf(stderr, "err: %d", state);
			FAIL("Failed to destroy appender");
		}
		if (i % 500 == 0) {
			printf("completed %d\n", i);
			duckdb_query(con, "checkpoint", NULL);
		}
	}

	// cleanup
	duckdb_disconnect(&con);
	duckdb_close(&db);
	REQUIRE(1 == 1);
}
