/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* Copyright (c) 2025 Brett A C Sheffield <bacs@librecast.net> */

/* test send functions return nett bytes sent, without overhead */

#include "test.h"
#include "testnet.h"
#include <librecast/net.h>
#include <librecast/key.h>

#define CHANNEL_NAME "0000-0084"

#ifdef HAVE_LIBLCRQ
static char data[BUFSIZ];

enum {
	SEND,
	RECV,
};

static int test_channel_send(int use_fec)
{
	lc_ctx_t *lctx;
	lc_socket_t *sock[2];
	lc_channel_t *chan[2];
	char buf[BUFSIZ];
	ssize_t byt;
	int rc;

	/* generate random data to send */
	ssize_t len = arc4random_uniform(sizeof data);
	arc4random_buf(data, len);

	lctx = lc_ctx_new();
	if (!test_assert(lctx != NULL, "lc_ctx_new()")) return test_status;
	rc = lc_socketpair(lctx, sock);
	if (!test_assert(rc == 0, "lc_socket_new()")) goto free_ctx;

	for (int i = 0; i < 2; i++) {
		chan[i] = lc_channel_new(lctx, CHANNEL_NAME);
		if (!test_assert(chan[i] != NULL, "lc_channel_new()[%i]", i)) goto free_ctx;
		rc = lc_channel_bind(sock[i], chan[i]);
		if (!test_assert(rc == 0, "lc_channel_bind()[%i]", i)) goto free_ctx;
		if (use_fec) {
			lc_channel_coding_set(chan[i], LC_CODE_FEC_RQ);
			lc_channel_rq_overhead(chan[i], RQ_OVERHEAD * 2);
		}
	}

	rc = lc_channel_join(chan[RECV]);
	if (!test_assert(rc == 0, "lc_channel_join()")) goto free_ctx;

	lc_keypair_t signerkey;
	lc_token_t tok = {0};
	lc_key_t ssk;

	rc = lc_keypair_new(&signerkey, LC_KEY_SIG);
	if (!test_assert(rc == 0, "lc_keypair_new()")) goto free_ctx;

	ssk.key = signerkey.sk;
	ssk.keylen = sizeof signerkey.sk;

	rc = lc_token_new(&tok, &signerkey, signerkey.pk, chan[0], 0, 0);
	if (!test_assert(rc == 0, "lc_token_new()")) goto free_ctx;

	lc_channel_setkey(chan[SEND], &ssk, LC_CODE_SIGN);
	lc_channel_token_set(chan[SEND], &tok);

	/* recv filter */
	lc_keyring_t keyring = {0};
	rc = lc_keyring_init(&keyring, 1);
	if (!test_assert(rc == 0, "lc_keyring_init()")) goto free_ctx;

	rc = lc_keyring_add(&keyring, signerkey.pk);
	if (!test_assert(rc == 0, "lc_keyring_add()")) goto free_keyring;

	lc_filter_t filter = { .keyring = &keyring };
	lc_channel_filter_set(chan[RECV], &filter);

	byt = lc_channel_send(chan[SEND], data, len, 0);
	test_assert(byt == len, "lc_channel_send() returned %zi (expected %zi)", byt, len);
	if (byt == -1 && errno == ENOBUFS) {
		test_status = TEST_WARN;
		goto free_keyring;
	}

	byt = lc_channel_recv(chan[RECV], buf, len, 0);
	test_assert(byt == len, "recv'ed %zi (expected %zi)", byt, len);

free_keyring:
	lc_keyring_free(&keyring);

free_ctx:
	lc_ctx_free(lctx);
	return test_status;
}
#endif

int main(void)
{
	char name[] = "check send/recv return values (token + filter)";
#ifdef HAVE_LIBLCRQ
	test_name(name);
	test_require_net(TEST_NET_BASIC);
	test_channel_send(0);
	test_channel_send(1);
	return test_status;
#else
	return test_skip(name);
#endif
}
