/*
 * Ibis - IRCv3 Library
 * Copyright (C) 2022-2024 Ibis Developers
 *
 * Ibis is the legal property of its developers, whose names are too numerous
 * to list here.  Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glib.h>

#include <ibis.h>

typedef struct {
	char *reply_type;
	char *command;
	char *code;
	const char * const context[16];
	char *description;
	gboolean error;
} TestIbisStandardReplyData;

/******************************************************************************
 * Helpers
 *****************************************************************************/
static void
test_ibis_standard_reply_parse(const char *raw_message,
                               TestIbisStandardReplyData *data)
{
	IbisMessage *message = NULL;
	IbisStandardReply *reply = NULL;
	GError *error = NULL;
	GStrv values = NULL;
	const char *value = NULL;

	message = ibis_message_parse(raw_message, &error);
	g_assert_no_error(error);
	g_assert_true(IBIS_IS_MESSAGE(message));

	reply = ibis_message_get_standard_reply(message);
	if(data->error) {
		g_assert_null(reply);

		g_assert_finalize_object(message);

		return;
	}

	g_assert_true(IBIS_IS_STANDARD_REPLY(reply));

	value = ibis_standard_reply_get_reply_type(reply);
	g_assert_cmpstr(value, ==, data->reply_type);

	g_assert_true(ibis_standard_reply_is_type(reply, data->reply_type));

	value = ibis_standard_reply_get_command(reply);
	g_assert_cmpstr(value, ==, data->command);

	value = ibis_standard_reply_get_code(reply);
	g_assert_cmpstr(value, ==, data->code);

	values = ibis_standard_reply_get_context(reply);
	g_assert_cmpstrv(values, data->context);

	value = ibis_standard_reply_get_description(reply);
	g_assert_cmpstr(value, ==, data->description);

	g_assert_finalize_object(message);
}

/******************************************************************************
 * Tests
 *****************************************************************************/
static void
test_ibis_standard_reply_parse_privmsg(void) {
	TestIbisStandardReplyData data = {
		.error = TRUE,
	};

	test_ibis_standard_reply_parse(":irene PRIVMSG #ibis :Hiya!", &data);
}

static void
test_ibis_standard_reply_parse_malformed(void) {
	TestIbisStandardReplyData data = {
		.error = TRUE,
	};

	/* Only has type. */
	test_ibis_standard_reply_parse("FAIL", &data);

	/* Missing code. */
	test_ibis_standard_reply_parse("FAIL *", &data);

	/* Missing description. */
	test_ibis_standard_reply_parse("FAIL * CODE", &data);
}

static void
test_ibis_standard_reply_parse_fail_need_registration(void) {
	TestIbisStandardReplyData data = {
		.reply_type = IBIS_STANDARD_REPLY_TYPE_FAIL,
		.command = "*",
		.code = "NEED_REGISTRATION",
		.description = "You need to be registered to continue",
	};

	test_ibis_standard_reply_parse("FAIL * NEED_REGISTRATION :You need to be registered to continue",
	                               &data);
}

static void
test_ibis_standard_reply_parse_note_account_passphrase_updated(void) {
	TestIbisStandardReplyData data = {
		.reply_type = IBIS_STANDARD_REPLY_TYPE_NOTE,
		.command = "AUTHENTICATE",
		.code = "ACCOUNT_PASSPHRASE_UPDATED",
		.description = "Your passphrase hash has been automatically updated to use our new, more secure method",
	};

	test_ibis_standard_reply_parse("NOTE AUTHENTICATE ACCOUNT_PASSPHRASE_UPDATED :Your passphrase hash has been automatically updated to use our new, more secure method",
	                               &data);
}

static void
test_ibis_standard_reply_parse_fail_reg_invalid_callback(void) {
	TestIbisStandardReplyData data = {
		.reply_type = IBIS_STANDARD_REPLY_TYPE_FAIL,
		.command = "ACC",
		.code = "REG_INVALID_CALLBACK",
		.context = {"REGISTER"},
		.description = "Email address is not valid",
	};

	test_ibis_standard_reply_parse("FAIL ACC REG_INVALID_CALLBACK REGISTER :Email address is not valid",
	                               &data);
}

static void
test_ibis_standard_reply_parse_fail_boxes_invalid(void) {
	TestIbisStandardReplyData data = {
		.reply_type = IBIS_STANDARD_REPLY_TYPE_FAIL,
		.command = "BOX",
		.code = "BOXES_INVALID",
		.context = {"STACK", "CLOCKWISE"},
		.description = "Given boxes are not supported",
	};

	test_ibis_standard_reply_parse("FAIL BOX BOXES_INVALID STACK CLOCKWISE :Given boxes are not supported",
	                               &data);
}

static void
test_ibis_standard_reply_parse_fail_config_bad(void) {
	TestIbisStandardReplyData data = {
		.reply_type = IBIS_STANDARD_REPLY_TYPE_FAIL,
		.command = "REHASH",
		.code = "CONFIG_BAD",
		.description = "Could not reload config from disk",
	};

	test_ibis_standard_reply_parse("FAIL REHASH CONFIG_BAD :Could not reload config from disk",
	                               &data);
}

static void
test_ibis_standard_reply_parse_warn_certs_expired(void) {
	TestIbisStandardReplyData data = {
		.reply_type = IBIS_STANDARD_REPLY_TYPE_WARN,
		.command = "REHASH",
		.code = "CERTS_EXPIRED",
		.description = "Certificate [blahblah.irc.example.com] has expired",
	};

	test_ibis_standard_reply_parse("WARN REHASH CERTS_EXPIRED :Certificate [blahblah.irc.example.com] has expired",
	                               &data);
}

static void
test_ibis_standard_reply_parse_note_oper_message(void) {
	TestIbisStandardReplyData data = {
		.reply_type = IBIS_STANDARD_REPLY_TYPE_NOTE,
		.command = "*",
		.code = "OPER_MESSAGE",
		.description = "Registering new accounts and channels has been disabled temporarily while we deal with the spam. Thanks for flying ExampleNet! -dan",
	};

	test_ibis_standard_reply_parse("NOTE * OPER_MESSAGE :Registering new accounts and channels has been disabled temporarily while we deal with the spam. Thanks for flying ExampleNet! -dan",
	                               &data);
}

static void
test_ibis_standard_reply_parse_fail_account_required_to_connect(void) {
	TestIbisStandardReplyData data = {
		.reply_type = IBIS_STANDARD_REPLY_TYPE_FAIL,
		.command = "*",
		.code = "ACCOUNT_REQUIRED_TO_CONNECT",
		.description = "An account is required to connect to the network",
	};

	test_ibis_standard_reply_parse("FAIL * ACCOUNT_REQUIRED_TO_CONNECT :An account is required to connect to the network",
	                               &data);
}

/******************************************************************************
 * Main
 *****************************************************************************/
int
main(int argc, char *argv[]) {
	g_test_init(&argc, &argv, NULL);
	g_test_set_nonfatal_assertions();

	g_test_add_func("/ibis/standard-reply/parse/privmsg",
	                test_ibis_standard_reply_parse_privmsg);
	g_test_add_func("/ibis/standard-reply/parse/malformed",
	                test_ibis_standard_reply_parse_malformed);

	/* These tests are based on the examples in the specification at
	 * https://ircv3.net/specs/extensions/standard-replies.
	 */
	g_test_add_func("/ibis/standard-reply/parse/fail-need-registration",
	                test_ibis_standard_reply_parse_fail_need_registration);
	g_test_add_func("/ibis/standard-reply/parse/node-account-passphrase-updated",
	                test_ibis_standard_reply_parse_note_account_passphrase_updated);
	g_test_add_func("/ibis/standard-reply/parse/fail-reg-invalid-callback",
	                test_ibis_standard_reply_parse_fail_reg_invalid_callback);
	g_test_add_func("/ibis/standard-reply/parse/fail-boxes-invalid",
	                test_ibis_standard_reply_parse_fail_boxes_invalid);
	g_test_add_func("/ibis/standard-reply/parse/fail-config-bad",
	                test_ibis_standard_reply_parse_fail_config_bad);
	g_test_add_func("/ibis/standard-reply/parse/warn-certs-expired",
	                test_ibis_standard_reply_parse_warn_certs_expired);
	g_test_add_func("/ibis/standard-reply/parse/note-oper-message",
	                test_ibis_standard_reply_parse_note_oper_message);
	g_test_add_func("/ibis/standard-reply/parse/fail-account-required-to-connect",
	                test_ibis_standard_reply_parse_fail_account_required_to_connect);

	return g_test_run();
}
