/*
 * Purple - Internet Messaging Library
 * Copyright (C) Pidgin Developers <devel@pidgin.im>
 *
 * 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 <birb.h>

#include <purple.h>

/******************************************************************************
 * Add/Remove Test
 *****************************************************************************/
static void
test_purple_contact_manager_seagull_backend_load_contacts_empty_cb(GObject *source,
                                                                   GAsyncResult *result,
                                                                   G_GNUC_UNUSED gpointer data)
{
	PurpleContactManagerBackend *backend = NULL;
	GError *error = NULL;
	PurpleContacts *contacts = NULL;

	birb_assert_type(source, PURPLE_TYPE_CONTACT_MANAGER_SEAGULL_BACKEND);
	backend = PURPLE_CONTACT_MANAGER_BACKEND(source);

	contacts = purple_contact_manager_backend_load_contacts_finish(backend,
	                                                               result,
	                                                               &error);
	g_assert_no_error(error);

	birb_assert_type(contacts, PURPLE_TYPE_CONTACTS);
	birb_assert_list_model_n_items(G_LIST_MODEL(contacts), 0);

	g_clear_object(&contacts);
}

static void
test_purple_contact_manager_seagull_backend_load_single_contact_cb(GObject *source,
                                                                   GAsyncResult *result,
                                                                   gpointer data)
{
	PurpleContactManagerBackend *backend = NULL;
	PurpleContact *actual = NULL;
	PurpleContact *expected = data;
	PurpleContactInfo *actual_info = NULL;
	PurpleContactInfo *expected_info = NULL;
	PurpleContacts *contacts = NULL;
	PurplePresence *actual_presence = NULL;
	PurplePresence *expected_presence = NULL;
	PurpleTags *actual_tags = NULL;
	PurpleTags *expected_tags = NULL;
	GDateTime *actual_date_time = NULL;
	GDateTime *expected_date_time = NULL;
	GError *error = NULL;

	birb_assert_type(source, PURPLE_TYPE_CONTACT_MANAGER_SEAGULL_BACKEND);
	backend = PURPLE_CONTACT_MANAGER_BACKEND(source);

	contacts = purple_contact_manager_backend_load_contacts_finish(backend,
	                                                               result,
	                                                               &error);
	g_assert_no_error(error);

	birb_assert_type(contacts, PURPLE_TYPE_CONTACTS);
	birb_assert_list_model_n_items(G_LIST_MODEL(contacts), 1);

	actual = g_list_model_get_item(G_LIST_MODEL(contacts), 0);
	birb_assert_type(actual, PURPLE_TYPE_CONTACT);

	g_assert_true(purple_contact_equal(actual, expected));

	actual_info = PURPLE_CONTACT_INFO(actual);
	expected_info = PURPLE_CONTACT_INFO(expected);

	/* purple_contact_info_equal only checks if the sort orders are equal so
	 * we need to check each property ourselves.
	 */
	g_assert_cmpstr(purple_contact_info_get_alias(actual_info), ==,
	                purple_contact_info_get_alias(expected_info));

	g_assert_cmpstr(purple_contact_info_get_color(actual_info), ==,
	                purple_contact_info_get_color(expected_info));

	g_assert_cmpstr(purple_contact_info_get_email(actual_info), ==,
	                purple_contact_info_get_email(expected_info));

	g_assert_true(purple_contact_info_get_external(actual_info) ==
	              purple_contact_info_get_external(expected_info));

	g_assert_true(purple_contact_info_get_favorite(actual_info) ==
	              purple_contact_info_get_favorite(expected_info));

	g_assert_cmpstr(purple_contact_info_get_id(actual_info), ==,
	                purple_contact_info_get_id(expected_info));

	g_assert_cmpstr(purple_contact_info_get_note(actual_info), ==,
	                purple_contact_info_get_note(expected_info));

	g_assert_cmpint(purple_contact_info_get_permission(actual_info), ==,
	                purple_contact_info_get_permission(expected_info));

	g_assert_cmpstr(purple_contact_info_get_phone_number(actual_info), ==,
	                purple_contact_info_get_phone_number(expected_info));

	g_assert_cmpstr(purple_contact_info_get_sid(actual_info), ==,
	                purple_contact_info_get_sid(expected_info));

	g_assert_cmpstr(g_time_zone_get_identifier(purple_contact_info_get_time_zone(actual_info)),
	                ==, g_time_zone_get_identifier(purple_contact_info_get_time_zone(expected_info)));

	actual_date_time = purple_contact_info_get_created_time(actual_info);
	expected_date_time = purple_contact_info_get_created_time(expected_info);
	g_assert_true(birb_date_time_equal(actual_date_time, expected_date_time));

	/* purple_presence_compare is meant for sorting so we need to check
	 * everything individually as well.
	 */
	actual_presence = purple_contact_info_get_presence(actual_info);
	expected_presence = purple_contact_info_get_presence(expected_info);

	g_assert_cmpstr(purple_presence_get_emoji(actual_presence), ==,
	                purple_presence_get_emoji(expected_presence));
	g_assert_true(purple_presence_is_idle(actual_presence) ==
	              purple_presence_is_idle(expected_presence));

	actual_date_time = purple_presence_get_idle_time(actual_presence);
	expected_date_time = purple_presence_get_idle_time(expected_presence);
	g_assert_true(birb_date_time_equal(actual_date_time, expected_date_time));

	actual_date_time = purple_presence_get_login_time(actual_presence);
	expected_date_time = purple_presence_get_login_time(expected_presence);
	g_assert_true(birb_date_time_equal(actual_date_time, expected_date_time));

	g_assert_cmpstr(purple_presence_get_message(actual_presence), ==,
	                purple_presence_get_message(expected_presence));
	g_assert_true(purple_presence_get_mobile(actual_presence) ==
	              purple_presence_get_mobile(expected_presence));
	g_assert_true(purple_presence_get_notifications_disabled(actual_presence)
	              ==
	              purple_presence_get_notifications_disabled(expected_presence));
	g_assert_cmpint(purple_presence_get_primitive(actual_presence), ==,
	                purple_presence_get_primitive(expected_presence));

#pragma message "Fix this when badges are persistent."
#if 0
	actual_badges = purple_contact_info_get_badges(actual_info);
	expected_badges = purple_contact_info_get_badges(expected_info);
	g_assert_cmpint(purple_badges_compare(actual_badges, expected_badges), ==,
	                0);
#endif

	actual_tags = purple_contact_info_get_tags(actual_info);
	expected_tags = purple_contact_info_get_tags(expected_info);

	g_assert_true(purple_tags_contains(actual_tags, expected_tags));

	g_clear_object(&actual);
	g_clear_object(&contacts);
}

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

	birb_assert_type(source, PURPLE_TYPE_CONTACT_MANAGER_SEAGULL_BACKEND);
	backend = PURPLE_CONTACT_MANAGER_BACKEND(source);

	success = purple_contact_manager_backend_save_contact_finish(backend,
	                                                             result,
	                                                             &error);
	g_assert_no_error(error);
	g_assert_true(success);

	purple_contact_manager_backend_load_contacts_async(backend,
	                                                   NULL,
	                                                   test_purple_contact_manager_seagull_backend_load_single_contact_cb,
	                                                   data);

	g_main_context_iteration(NULL, FALSE);
}

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

	birb_assert_type(source, PURPLE_TYPE_CONTACT_MANAGER_SEAGULL_BACKEND);
	backend = PURPLE_CONTACT_MANAGER_BACKEND(source);

	success = purple_contact_manager_backend_delete_contact_finish(backend,
	                                                               result,
	                                                               &error);
	g_assert_no_error(error);
	g_assert_true(success);

	purple_contact_manager_backend_load_contacts_async(backend,
	                                                   NULL,
	                                                   test_purple_contact_manager_seagull_backend_load_contacts_empty_cb,
	                                                   NULL);

	g_main_context_iteration(NULL, FALSE);
}

static void
test_purple_contact_manager_seagull_backend_add_remove_contact(void) {
	PurpleContactManagerBackend *backend = NULL;
	PurpleAccount *account = NULL;
	PurpleBadge *badge = NULL;
	PurpleBadges *badges = NULL;
	PurpleContact *contact = NULL;
	PurplePresence *presence = NULL;
	PurpleTags *tags = NULL;
	GDateTime *date_time = NULL;
	GDateTime *now = NULL;
	GError *error = NULL;
	GTimeZone *tz = NULL;

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

	birb_assert_type(backend, PURPLE_TYPE_CONTACT_MANAGER_SEAGULL_BACKEND);

	/* Create our contact for testing. */
	tz = g_time_zone_new_utc();
	date_time = g_date_time_new_from_iso8601("19981106T000000-0600", NULL);
	account = purple_account_new("test", "test");
	contact = g_object_new(
		PURPLE_TYPE_CONTACT,
		"account", account,
		"alias", "The Purple Pigeon",
		"color", "#eff1ce",
		"created-time", date_time,
		"display-name", "Pidgy",
		"email", "pidgy@pidgin.im",
		"external", TRUE,
		"favorite", TRUE,
		"id", "0e69395d-cd8f-4b6f-b26f-ae15bdbb9a36",
		"note", "brand account",
		"permission", PURPLE_CONTACT_INFO_PERMISSION_ALLOW,
		"phone-number", "+15558675309",
		"sid", "the sloth",
		"time-zone", tz,
		NULL);
	birb_date_time_clear(&date_time);
	g_clear_pointer(&tz, g_time_zone_unref);

	presence = purple_contact_info_get_presence(PURPLE_CONTACT_INFO(contact));
	purple_presence_set_emoji(presence, "🐧");
	purple_presence_set_idle(presence, TRUE);

	now = g_date_time_new_now_utc();
	date_time = g_date_time_add_minutes(now, -42);
	purple_presence_set_idle_time(presence, date_time);
	birb_date_time_clear(&date_time);

	date_time = g_date_time_add_minutes(now, -1337);
	purple_presence_set_login_time(presence, date_time);
	birb_date_time_clear(&date_time);

	birb_date_time_clear(&now);

	purple_presence_set_message(presence, "doing stuff...");
	purple_presence_set_mobile(presence, TRUE);
	purple_presence_set_notifications_disabled(presence, TRUE);
	purple_presence_set_primitive(presence,
	                              PURPLE_PRESENCE_PRIMITIVE_STREAMING);

	badges = purple_contact_info_get_badges(PURPLE_CONTACT_INFO(contact));
	badge = purple_badge_new("test-badge", 0, "test", "t");
	purple_badges_add_badge(badges, badge);
	g_clear_object(&badge);

	tags = purple_contact_info_get_tags(PURPLE_CONTACT_INFO(contact));
	purple_tags_add(tags, "test=1");

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

	g_main_context_iteration(NULL, FALSE);

	/* Save the presence. */
	purple_contact_manager_backend_save_contact_async(backend,
	                                                  contact,
	                                                  NULL,
	                                                  test_purple_contact_manager_seagull_backend_save_contact_cb,
	                                                  contact);

	g_main_context_iteration(NULL, FALSE);

	/* Delete the presence. */
	purple_contact_manager_backend_delete_contact_async(backend,
	                                                    contact,
	                                                    NULL,
	                                                    test_purple_contact_manager_seagull_backend_delete_contact_cb,
	                                                    NULL);

	g_main_context_iteration(NULL, FALSE);

	/* Clean up */
	g_assert_finalize_object(contact);
	g_assert_finalize_object(account);
	g_assert_finalize_object(backend);
}

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

	g_test_add_func("/contact-manager-seagull-backend/add-remove-contact",
	                test_purple_contact_manager_seagull_backend_add_remove_contact);

	return g_test_run();
}
