/*
 * Purple - Internet Messaging Library
 * Copyright (C) Pidgin Developers <devel@pidgin.im>
 *
 * Purple 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 program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glib.h>

#include <birb.h>

#include <seagull.h>

#include <purple.h>

#include "test_ui.h"

#define TEST_PURPLE_CONVERSATION_MANAGER_SEAGULL_BACKEND_ERROR (g_quark_from_static_string("test-contact-manager-seagull-backend_error"))

/******************************************************************************
 * Tests
 *****************************************************************************/
static void
test_purple_conversation_manager_seagull_backend_new(void) {
	PurpleConversationManagerBackend *backend = NULL;
	GError *error = NULL;

	backend = purple_conversation_manager_seagull_backend_new(NULL, &error);
	g_assert_no_error(error);

	birb_assert_type(backend,
	                 PURPLE_TYPE_CONVERSATION_MANAGER_SEAGULL_BACKEND);

	g_assert_finalize_object(backend);
}

/******************************************************************************
 * Add/Remove Test
 *****************************************************************************/
static void
test_purple_conversation_manager_seagull_backend_load_conversations_empty_cb(GObject *source,
                                                                             GAsyncResult *result,
                                                                             G_GNUC_UNUSED gpointer data)
{
	GError *error = NULL;
	GListModel *conversations = NULL;

	birb_assert_type(source, PURPLE_TYPE_CONVERSATION_MANAGER_SEAGULL_BACKEND);

	conversations = purple_conversation_manager_backend_load_conversations_finish(PURPLE_CONVERSATION_MANAGER_BACKEND(source),
	                                                                              result,
	                                                                              &error);
	g_assert_no_error(error);
	g_assert_true(G_IS_LIST_MODEL(conversations));

	birb_assert_list_model_n_items(conversations, 0);

	g_assert_finalize_object(conversations);
}

static void
test_purple_conversation_manager_seagull_backend_load_conversations_single_cb(GObject *source,
                                                                              GAsyncResult *result,
                                                                              gpointer data)
{
	PurpleAccount *actual_account = NULL;
	PurpleAccount *expected_account = NULL;
	PurpleContactInfo *actual_info = NULL;
	PurpleContactInfo *expected_info = NULL;
	PurpleConversation *actual = NULL;
	PurpleConversation *expected = data;
	PurpleConversationManagerBackend *backend = NULL;
	PurpleTags *actual_tags = NULL;
	PurpleTags *expected_tags = NULL;
	GError *error = NULL;
	GError *actual_error = NULL;
	GError *expected_error = NULL;
	GListModel *conversations = NULL;
	GDateTime *actual_date_time = NULL;
	GDateTime *expected_date_time = NULL;

	birb_assert_type(source, PURPLE_TYPE_CONVERSATION_MANAGER_SEAGULL_BACKEND);
	backend = PURPLE_CONVERSATION_MANAGER_BACKEND(source);

	conversations = purple_conversation_manager_backend_load_conversations_finish(backend,
	                                                                              result,
	                                                                              &error);
	g_assert_no_error(error);
	g_assert_true(G_IS_LIST_MODEL(conversations));

	birb_assert_list_model_n_items(G_LIST_MODEL(conversations), 1);

	actual = g_list_model_get_item(G_LIST_MODEL(conversations), 0);
	birb_assert_type(actual, PURPLE_TYPE_CONVERSATION);

	/* Run through the conversation properties. */
	expected_account = purple_conversation_get_account(expected);
	actual_account = purple_conversation_get_account(actual);
	g_assert_true(purple_account_equal(actual_account, expected_account));

	g_assert_true(purple_conversation_get_age_restricted(actual) ==
	              purple_conversation_get_age_restricted(expected));

	g_assert_cmpstr(purple_conversation_get_alias(actual), ==,
	                purple_conversation_get_alias(expected));

	g_assert_true(purple_conversation_get_can_send_message(actual) ==
	              purple_conversation_get_can_send_message(expected));

	g_assert_cmpstr(purple_conversation_get_color(actual), ==,
	                purple_conversation_get_color(expected));

	actual_date_time = purple_conversation_get_created_on(actual);
	expected_date_time = purple_conversation_get_created_on(expected);
	g_assert_true(birb_date_time_equal(actual_date_time, expected_date_time));

	actual_info = purple_conversation_get_creator(actual);
	expected_info = purple_conversation_get_creator(expected);
	g_assert_true(purple_contact_info_equal(actual_info, expected_info));

	g_assert_cmpstr(purple_conversation_get_description(actual), ==,
	                purple_conversation_get_description(expected));

	g_assert_true(purple_conversation_get_drafting(actual) ==
	              purple_conversation_get_drafting(expected));

	actual_error = purple_conversation_get_error(actual);
	expected_error = purple_conversation_get_error(expected);
	if(expected_error != NULL) {
		g_assert_error(actual_error, expected_error->domain,
		               expected_error->code);
	} else {
		g_assert_no_error(actual_error);
	}

	g_assert_true(purple_conversation_get_favorite(actual) ==
	              purple_conversation_get_favorite(expected));

	g_assert_true(purple_conversation_get_federated(actual) ==
	              purple_conversation_get_federated(expected));

	g_assert_cmpstr(purple_conversation_get_id(actual), ==,
	                purple_conversation_get_id(expected));

	g_assert_true(purple_conversation_get_logging(actual) ==
	              purple_conversation_get_logging(expected));

	g_assert_true(purple_conversation_get_needs_attention(actual) ==
	              purple_conversation_get_needs_attention(expected));

	g_assert_cmpstr(purple_conversation_get_sid(actual), ==,
	                purple_conversation_get_sid(expected));

	g_assert_cmpstr(purple_conversation_get_status_message(actual), ==,
	                purple_conversation_get_status_message(expected));

	g_assert_cmpstr(purple_conversation_get_title(actual), ==,
	                purple_conversation_get_title(expected));

	g_assert_cmpstr(purple_conversation_get_topic(actual), ==,
	                purple_conversation_get_topic(expected));

	actual_info = purple_conversation_get_topic_author(actual);
	expected_info = purple_conversation_get_topic_author(expected);
	g_assert_true(purple_contact_info_equal(actual_info, expected_info));

	actual_date_time = purple_conversation_get_topic_updated(actual);
	expected_date_time = purple_conversation_get_topic_updated(expected);
	g_assert_true(birb_date_time_equal(actual_date_time, expected_date_time));

	g_assert_cmpuint(purple_conversation_get_conversation_type(actual), ==,
	                 purple_conversation_get_conversation_type(expected));

	g_assert_cmpstr(purple_conversation_get_url(actual), ==,
	                purple_conversation_get_url(expected));

	g_assert_cmpstr(purple_conversation_get_user_nickname(actual), ==,
	                purple_conversation_get_user_nickname(expected));

	/* Verify the tags. */
	actual_tags = purple_conversation_get_tags(actual);
	expected_tags = purple_conversation_get_tags(expected);

	g_assert_true(purple_tags_contains(actual_tags, expected_tags));

	/* Cleanup. */
	g_assert_finalize_object(conversations);
	g_assert_finalize_object(actual);
}

static void
test_purple_conversation_manager_seagull_backend_save_cb(GObject *source,
                                                         GAsyncResult *result,
                                                         gpointer data)
{
	PurpleConversationManagerBackend *backend = NULL;
	GError *error = NULL;
	gboolean success = FALSE;

	backend = PURPLE_CONVERSATION_MANAGER_BACKEND(source);
	success = purple_conversation_manager_backend_save_conversation_finish(backend,
	                                                                       result,
	                                                                       &error);
	g_assert_no_error(error);
	g_assert_true(success);

	purple_conversation_manager_backend_load_conversations_async(backend, NULL,
	                                                             test_purple_conversation_manager_seagull_backend_load_conversations_single_cb,
	                                                             data);

	g_main_context_iteration(NULL, FALSE);
}

static void
test_purple_conversation_manager_seagull_backend_delete_cb(GObject *source,
                                                           GAsyncResult *result,
                                                           G_GNUC_UNUSED gpointer data)
{
	PurpleConversationManagerBackend *backend = NULL;
	GError *error = NULL;
	gboolean success = FALSE;

	backend = PURPLE_CONVERSATION_MANAGER_BACKEND(source);
	success = purple_conversation_manager_backend_delete_conversation_finish(backend,
	                                                                         result,
	                                                                         &error);
	g_assert_no_error(error);
	g_assert_true(success);

	purple_conversation_manager_backend_load_conversations_async(backend, NULL,
	                                                             test_purple_conversation_manager_seagull_backend_load_conversations_empty_cb,
	                                                             NULL);

	g_main_context_iteration(NULL, FALSE);
}

static void
test_purple_conversation_manager_seagull_backend_add_remove(void) {
	PurpleAccount *account = NULL;
	PurpleAccountManager *account_manager = NULL;
	PurpleContactInfo *info = NULL;
	PurpleConversation *conversation = NULL;
	PurpleConversationManagerBackend *backend = NULL;
	PurpleCore *core = NULL;
	PurpleTags *tags = NULL;
	GDateTime *created_on = NULL;
	GDateTime *topic_updated = NULL;
	GError *conversation_error = NULL;
	GError *error = NULL;

	core = purple_core_get_default();
	account_manager = purple_core_get_account_manager(core);

	backend = purple_conversation_manager_seagull_backend_new(NULL, &error);
	g_assert_no_error(error);
	birb_assert_type(backend, PURPLE_TYPE_CONVERSATION_MANAGER_SEAGULL_BACKEND);

	account = g_object_new(PURPLE_TYPE_ACCOUNT,
	                       "name", "Guy Fawkes",
	                       "protocol-id", "prpl-demo",
	                       NULL);
	purple_account_manager_add(account_manager, account);
	info = purple_account_get_contact_info(account);
	purple_contact_info_set_id(info, "1605-11-05");

	conversation_error = g_error_new(TEST_PURPLE_CONVERSATION_MANAGER_SEAGULL_BACKEND_ERROR,
	                                 0, "bleh");

	created_on = g_date_time_new_from_iso8601("1605-11-05T00:00:00Z", NULL);
	topic_updated = g_date_time_new_from_iso8601("2063-04-05T00:00:00Z", NULL);

	conversation = g_object_new(PURPLE_TYPE_CONVERSATION,
	                            "account", account,
	                            "age-restricted", TRUE,
	                            "alias", "alias1",
	                            "can-send-message", TRUE,
	                            "color", "blue",
	                            "created-on", created_on,
	                            "creator", info,
	                            "description", "This chat is a chat",
	                            "drafting", TRUE,
	                            "error", conversation_error,
	                            "favorite", TRUE,
	                            "federated", TRUE,
	                            "id", "id1",
	                            "logging", TRUE,
	                            "needs-attention", TRUE,
	                            "online", TRUE,
	                            "sid", "sid-vicious",
	                            "status-message", "this is a status",
	                            "title", "title1",
	                            "topic", "topic1",
	                            "topic-author", info,
	                            "topic-updated", topic_updated,
	                            "type", PURPLE_CONVERSATION_TYPE_CHANNEL,
	                            "url", "http://example.com/chat/",
	                            "user-nickname", "nicked",
	                            NULL);
	g_clear_error(&conversation_error);
	birb_date_time_clear(&created_on);
	birb_date_time_clear(&topic_updated);

	tags = purple_conversation_get_tags(conversation);
	purple_tags_add(tags, "test=1");

	/* Make sure that there's nothing to load. */
	purple_conversation_manager_backend_load_conversations_async(backend, NULL,
	                                                             test_purple_conversation_manager_seagull_backend_load_conversations_empty_cb,
	                                                             NULL);

	g_main_context_iteration(NULL, FALSE);

	/* Save the conversation. */
	purple_conversation_manager_backend_save_conversation_async(backend,
	                                                            conversation,
	                                                            NULL,
	                                                            test_purple_conversation_manager_seagull_backend_save_cb,
	                                                            conversation);

	g_main_context_iteration(NULL, FALSE);

	/* Delete the conversation from the backend. */
	purple_conversation_manager_backend_delete_conversation_async(backend,
	                                                              conversation,
	                                                              NULL,
	                                                              test_purple_conversation_manager_seagull_backend_delete_cb,
	                                                              NULL);

	g_main_context_iteration(NULL, FALSE);

	/* Cleanup. */
	g_assert_finalize_object(backend);
	g_assert_finalize_object(conversation);

	purple_account_manager_remove(account_manager, account);
	g_assert_finalize_object(account);
}

/******************************************************************************
 * Public API
 *****************************************************************************/
int
main(int argc, char **argv) {
	GError *error = NULL;
	int ret = 0;

	g_test_init(&argc, &argv, NULL);
	g_test_set_nonfatal_assertions();

	test_ui_purple_init(&error);
	g_assert_no_error(error);

	g_test_add_func("/conversation-manager-backend/seagull/new",
	                test_purple_conversation_manager_seagull_backend_new);

	g_test_add_func("/conversation-manager-backend/seagull/add-remove",
	                test_purple_conversation_manager_seagull_backend_add_remove);

	ret = g_test_run();

	test_ui_purple_uninit();

	return ret;
}
