/*
 * Copyright (c) 2026 Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3.0 only,
 * as published by the Free Software Foundation.
 *
 * 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 Lesser General Public License
 * version 3.0 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#ifdef __OBJC__
# error These tests should explicitly be compiled with a pure C compiler
#endif

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "Gemtext2HTML.h"

static void
check_line(G2HConverter *converter, const char *line, const char *expected)
{
	const char *got = g2h_converter_process_line(converter, line);
	assert(got != NULL);

	if (strcmp(got, expected) != 0) {
		fprintf(stderr, "[FAIL] Line:     %s\n", line);
		fprintf(stderr, "[FAIL] Expected: %s\n", expected);
		fprintf(stderr, "[FAIL] Got:      %s\n", got);
		exit(1);
	}
}

static void
check_finish(G2HConverter *converter, const char *expected)
{
	const char *got = g2h_converter_finish_processing(converter);
	assert(got != NULL);

	if (strcmp(got, expected) != 0) {
		fprintf(stderr, "[FAIL] Expected finish: %s\n", expected);
		fprintf(stderr, "[FAIL] Got finish:      %s\n", got);
		exit(1);
	}
}

static void
test_text(void)
{
	G2HConverter *converter = g2h_converter_new();
	assert(converter != NULL);

	check_line(converter, "asd<foo", "asd&lt;foo<br/>");
	check_finish(converter, "");

	g2h_converter_free(converter);
}

static void
test_headings(void)
{
	G2HConverter *converter = g2h_converter_new();
	assert(converter != NULL);

	check_line(converter, "# asd<foo",
	    "<h1 class='gemtext-heading1'> asd&lt;foo</h1>");
	check_line(converter, "## asd<foo",
	    "<h2 class='gemtext-heading2'> asd&lt;foo</h2>");
	check_line(converter, "### asd<foo",
	    "<h3 class='gemtext-heading3'> asd&lt;foo</h3>");
	check_finish(converter, "");

	g2h_converter_free(converter);
}

static void
test_local_link(void)
{
	G2HConverter *converter = g2h_converter_new();
	assert(converter != NULL);

	check_line(converter, "=>f<oo",
	    "<span class='gemtext-local-link-emoji'>👉</span> "
	    "<a class='gemtext-local-link' href='f&lt;oo'>f&lt;oo</a><br/>");

	check_line(converter, "=> f<oo",
	    "<span class='gemtext-local-link-emoji'>👉</span> "
	    "<a class='gemtext-local-link' href='f&lt;oo'>f&lt;oo</a><br/>");

	check_line(converter, "=> 	 f<oo",
	    "<span class='gemtext-local-link-emoji'>👉</span> "
	    "<a class='gemtext-local-link' href='f&lt;oo'>f&lt;oo</a><br/>");

	check_line(converter, "=> 	 f<oo 	 ",
	    "<span class='gemtext-local-link-emoji'>👉</span> "
	    "<a class='gemtext-local-link' href='f&lt;oo'>f&lt;oo</a><br/>");

	check_finish(converter, "");

	g2h_converter_free(converter);
}

static void
test_external_link(void)
{
	G2HConverter *converter = g2h_converter_new();
	assert(converter != NULL);

	check_line(converter, "=>gemini://f<oo",
	    "<span class='gemtext-external-link-emoji'>🌐</span> "
	    "<a class='gemtext-external-link' href='gemini://f&lt;oo'>"
	    "gemini://f&lt;oo</a><br/>");

	check_line(converter, "=> gemini://f<oo",
	    "<span class='gemtext-external-link-emoji'>🌐</span> "
	    "<a class='gemtext-external-link' href='gemini://f&lt;oo'>"
	    "gemini://f&lt;oo</a><br/>");

	check_line(converter, "=> 	 gemini://f<oo",
	    "<span class='gemtext-external-link-emoji'>🌐</span> "
	    "<a class='gemtext-external-link' href='gemini://f&lt;oo'>"
	    "gemini://f&lt;oo</a><br/>");

	check_line(converter, "=> 	 gemini://f<oo 	 ",
	    "<span class='gemtext-external-link-emoji'>🌐</span> "
	    "<a class='gemtext-external-link' href='gemini://f&lt;oo'>"
	    "gemini://f&lt;oo</a><br/>");

	check_finish(converter, "");

	g2h_converter_free(converter);
}

static void
test_local_link_with_description(void)
{
	G2HConverter *converter = g2h_converter_new();
	assert(converter != NULL);

	check_line(converter, "=>f<oo b<ar",
	    "<span class='gemtext-local-link-emoji'>👉</span> "
	    "<a class='gemtext-local-link' href='f&lt;oo'>b&lt;ar</a><br/>");

	check_line(converter, "=> f<oo b<ar",
	    "<span class='gemtext-local-link-emoji'>👉</span> "
	    "<a class='gemtext-local-link' href='f&lt;oo'>b&lt;ar</a><br/>");

	check_line(converter, "=> 	 f<oo 	 b<ar",
	    "<span class='gemtext-local-link-emoji'>👉</span> "
	    "<a class='gemtext-local-link' href='f&lt;oo'>b&lt;ar</a><br/>");

	check_line(converter, "=> 	 f<oo 	 b<ar 	 ",
	    "<span class='gemtext-local-link-emoji'>👉</span> "
	    "<a class='gemtext-local-link' href='f&lt;oo'>b&lt;ar 	 </a>"
	    "<br/>");

	check_finish(converter, "");

	g2h_converter_free(converter);
}

static void
test_external_link_with_description(void)
{
	G2HConverter *converter = g2h_converter_new();
	assert(converter != NULL);

	check_line(converter, "=>gemini://f<oo b<ar",
	    "<span class='gemtext-external-link-emoji'>🌐</span> "
	    "<a class='gemtext-external-link' href='gemini://f&lt;oo'>"
	    "b&lt;ar</a><br/>");

	check_line(converter, "=> gemini://f<oo b<ar",
	    "<span class='gemtext-external-link-emoji'>🌐</span> "
	    "<a class='gemtext-external-link' href='gemini://f&lt;oo'>"
	    "b&lt;ar</a><br/>");

	check_line(converter, "=> 	 gemini://f<oo 	 b<ar",
	    "<span class='gemtext-external-link-emoji'>🌐</span> "
	    "<a class='gemtext-external-link' href='gemini://f&lt;oo'>"
	    "b&lt;ar</a><br/>");

	check_line(converter, "=> 	 gemini://f<oo 	 b<ar 	 ",
	    "<span class='gemtext-external-link-emoji'>🌐</span> "
	    "<a class='gemtext-external-link' href='gemini://f&lt;oo'>"
	    "b&lt;ar 	 </a><br/>");

	check_finish(converter, "");

	g2h_converter_free(converter);
}

static void
test_local_image_link(void)
{
	G2HConverter *converter = g2h_converter_new();
	assert(converter != NULL);

	check_line(converter, "=>f<oo.png",
	    "<img class='gemtext-image' src='f&lt;oo.png'/><br/>");

	check_line(converter, "=> f<oo.png?asd",
	    "<img class='gemtext-image' src='f&lt;oo.png?asd'/><br/>");

	check_line(converter, "=> 	 f<oo.PNG",
	    "<img class='gemtext-image' src='f&lt;oo.PNG'/><br/>");

	check_line(converter, "=> 	 f<oo.png 	 ",
	    "<img class='gemtext-image' src='f&lt;oo.png'/><br/>");

	check_finish(converter, "");

	g2h_converter_free(converter);
}

static void
test_external_image_link(void)
{
	G2HConverter *converter = g2h_converter_new();
	assert(converter != NULL);

	check_line(converter, "=>gemini://x/f<oo.png",
	    "<img class='gemtext-image' src='gemini://x/f&lt;oo.png'/><br/>");

	check_line(converter, "=> gemini://x/f<oo.png?asd",
	    "<img class='gemtext-image' src='gemini://x/f&lt;oo.png?asd'/>"
	    "<br/>");

	check_line(converter, "=> 	 gemini://x/f<oo.PNG",
	    "<img class='gemtext-image' src='gemini://x/f&lt;oo.PNG'/><br/>");

	check_line(converter, "=> 	 gemini://x/f<oo.png 	 ",
	    "<img class='gemtext-image' src='gemini://x/f&lt;oo.png'/><br/>");

	check_finish(converter, "");

	g2h_converter_free(converter);
}

static void
test_local_image_link_with_description(void)
{
	G2HConverter *converter = g2h_converter_new();
	assert(converter != NULL);

	check_line(converter, "=>f<oo.png b<ar",
	    "<img class='gemtext-image' src='f&lt;oo.png' alt='b&lt;ar'/>"
	    "<br/>");

	check_line(converter, "=> f<oo.png?asd b<ar",
	    "<img class='gemtext-image' src='f&lt;oo.png?asd' alt='b&lt;ar'/>"
	    "<br/>");

	check_line(converter, "=> 	 f<oo.PNG 	 b<ar",
	    "<img class='gemtext-image' src='f&lt;oo.PNG' alt='b&lt;ar'/>"
	    "<br/>");

	check_line(converter, "=> 	 f<oo.png 	 b<ar 	 ",
	    "<img class='gemtext-image' src='f&lt;oo.png' alt='b&lt;ar 	 '/>"
	    "<br/>");

	check_finish(converter, "");

	g2h_converter_free(converter);
}

static void
test_external_image_link_with_description(void)
{
	G2HConverter *converter = g2h_converter_new();
	assert(converter != NULL);

	check_line(converter, "=>gemini://x/f<oo.png b<ar",
	    "<img class='gemtext-image' src='gemini://x/f&lt;oo.png' "
	    "alt='b&lt;ar'/><br/>");

	check_line(converter, "=> gemini://x/f<oo.png?asd b<ar",
	    "<img class='gemtext-image' src='gemini://x/f&lt;oo.png?asd' "
	    "alt='b&lt;ar'/><br/>");

	check_line(converter, "=> 	 gemini://x/f<oo.PNG 	 b<ar",
	    "<img class='gemtext-image' src='gemini://x/f&lt;oo.PNG' "
	    "alt='b&lt;ar'/><br/>");

	check_line(converter, "=> 	 gemini://x/f<oo.png 	 b<ar 	 ",
	    "<img class='gemtext-image' src='gemini://x/f&lt;oo.png' "
	    "alt='b&lt;ar 	 '/><br/>");

	check_finish(converter, "");

	g2h_converter_free(converter);
}

static void
test_invalid_link(void)
{
	G2HConverter *converter = g2h_converter_new();
	assert(converter != NULL);

	assert(g2h_converter_process_line(converter, "=>") == NULL);
	assert(g2h_converter_process_line(converter, "=> 	 ") == NULL);

	g2h_converter_free(converter);
}

static void
test_list(void)
{
	G2HConverter *converter = g2h_converter_new();
	assert(converter != NULL);

	check_line(converter, "Foo", "Foo<br/>");
	check_line(converter, "* li<1",
	    "<ul class='gemtext-list' style='margin-top: 0; margin-bottom: 0'>"
	    "\n"
	    "<li class='gemtext-list-item'>li&lt;1</li>");
	check_line(converter, "* li<2",
	    "<li class='gemtext-list-item'>li&lt;2</li>");
	check_line(converter, "Bar",
	    "</ul>\n"
	    "Bar<br/>");

	check_line(converter, "* li<3",
	    "<ul class='gemtext-list' style='margin-top: 0; margin-bottom: 0'>"
	    "\n"
	    "<li class='gemtext-list-item'>li&lt;3</li>");
	check_finish(converter, "</ul>");

	g2h_converter_free(converter);
}

static void
test_quote(void)
{
	G2HConverter *converter = g2h_converter_new();
	assert(converter != NULL);

	check_line(converter, "Foo", "Foo<br/>");
	check_line(converter, ">q<1",
	    "<blockquote class='gemtext-quote' style='margin-top: 0; "
	    "margin-bottom: 0'>\n"
	    "q&lt;1<br/>");
	check_line(converter, "> q<2", " q&lt;2<br/>");
	check_line(converter, "Bar",
	    "</blockquote>\n"
	    "Bar<br/>");

	check_line(converter, ">q<3",
	    "<blockquote class='gemtext-quote' style='margin-top: 0; "
	    "margin-bottom: 0'>\n"
	    "q&lt;3<br/>");
	check_finish(converter, "</blockquote>");

	g2h_converter_free(converter);
}

static void
test_preformatted(void)
{
	G2HConverter *converter = g2h_converter_new();
	assert(converter != NULL);

	check_line(converter,"Foo", "Foo<br/>");
	check_line(converter, "```",
	    "<pre class='gemtext-preformatted' style='margin-top: 0; "
	    "margin-bottom: 0'>");
	check_line(converter, " B<ar", " B&lt;ar");
	check_line(converter, "B<az", "B&lt;az");
	check_line(converter, "```", "</pre>");
	check_line(converter, "Qux", "Qux<br/>");
	check_finish(converter, "");

	g2h_converter_free(converter);
}

static void
test_unclosed_preformatted(void)
{
	G2HConverter *converter = g2h_converter_new();
	assert(converter != NULL);

	check_line(converter, "```",
	    "<pre class='gemtext-preformatted' style='margin-top: 0; "
	    "margin-bottom: 0'>");
	assert(g2h_converter_finish_processing(converter) == NULL);

	g2h_converter_free(converter);
}

int
main()
{
	test_text();
	test_headings();
	test_local_link();
	test_external_link();
	test_local_link_with_description();
	test_external_link_with_description();
	test_local_image_link();
	test_external_image_link();
	test_local_image_link_with_description();
	test_external_image_link_with_description();
	test_invalid_link();
	test_list();
	test_quote();
	test_preformatted();
	test_unclosed_preformatted();

	return 0;
}
