/*
 * 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/>.
 */

#import <ObjFW/ObjFW.h>
#import <ObjFWTest/ObjFWTest.h>

#import "G2HConverter.h"

@interface G2HConverterTests: OTTestCase
{
	G2HConverter *_converter;
}
@end

@implementation G2HConverterTests
- (void)setUp
{
	_converter = [[G2HConverter alloc] init];
}

- (void)testText
{
	OTAssertEqualObjects([_converter processLine: @"asd<foo"],
	    @"asd&lt;foo<br/>");
	OTAssertEqualObjects([_converter finishProcessing], @"");
}

- (void)testHeadings
{
	OTAssertEqualObjects([_converter processLine: @"# asd<foo"],
	    @"<h1 class='gemtext-heading1'> asd&lt;foo</h1>");
	OTAssertEqualObjects([_converter processLine: @"## asd<foo"],
	    @"<h2 class='gemtext-heading2'> asd&lt;foo</h2>");
	OTAssertEqualObjects([_converter processLine: @"### asd<foo"],
	    @"<h3 class='gemtext-heading3'> asd&lt;foo</h3>");
	OTAssertEqualObjects([_converter finishProcessing], @"");
}

- (void)testLocalLink
{
	OTAssertEqualObjects([_converter processLine: @"=>f<oo"],
	    @"<span class='gemtext-local-link-emoji'>👉</span> "
	    @"<a class='gemtext-local-link' href='f&lt;oo'>f&lt;oo</a><br/>");

	OTAssertEqualObjects([_converter processLine: @"=> f<oo"],
	    @"<span class='gemtext-local-link-emoji'>👉</span> "
	    @"<a class='gemtext-local-link' href='f&lt;oo'>f&lt;oo</a><br/>");

	OTAssertEqualObjects([_converter processLine: @"=> 	 f<oo"],
	    @"<span class='gemtext-local-link-emoji'>👉</span> "
	    @"<a class='gemtext-local-link' href='f&lt;oo'>f&lt;oo</a><br/>");

	OTAssertEqualObjects([_converter processLine: @"=> 	 f<oo 	 "],
	    @"<span class='gemtext-local-link-emoji'>👉</span> "
	    @"<a class='gemtext-local-link' href='f&lt;oo'>"
	    @"f&lt;oo</a><br/>");

	OTAssertEqualObjects([_converter finishProcessing], @"");
}

- (void)testExternalLink
{
	OTAssertEqualObjects([_converter processLine: @"=>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/>");

	OTAssertEqualObjects([_converter processLine: @"=> 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/>");

	OTAssertEqualObjects(
	    [_converter processLine: @"=> 	 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/>");

	OTAssertEqualObjects(
	    [_converter processLine: @"=> 	 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/>");

	OTAssertEqualObjects([_converter finishProcessing], @"");
}

- (void)testLocalLinkWithDescription
{
	OTAssertEqualObjects([_converter processLine: @"=>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/>");

	OTAssertEqualObjects([_converter processLine: @"=> 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/>");

	OTAssertEqualObjects(
	    [_converter processLine: @"=> 	 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/>");

	OTAssertEqualObjects(
	    [_converter processLine: @"=> 	 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/>");

	OTAssertEqualObjects([_converter finishProcessing], @"");
}

- (void)testExternalLinkWithDescription
{
	OTAssertEqualObjects([_converter processLine: @"=>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/>");

	OTAssertEqualObjects([_converter processLine: @"=> 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/>");

	OTAssertEqualObjects(
	    [_converter processLine: @"=> 	 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/>");

	OTAssertEqualObjects(
	    [_converter processLine: @"=> 	 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/>");

	OTAssertEqualObjects([_converter finishProcessing], @"");
}

- (void)testLocalImageLink
{
	OTAssertEqualObjects([_converter processLine: @"=>f<oo.png"],
	    @"<img class='gemtext-image' src='f&lt;oo.png'/><br/>");

	OTAssertEqualObjects([_converter processLine: @"=> f<oo.png?asd"],
	    @"<img class='gemtext-image' src='f&lt;oo.png?asd'/><br/>");

	OTAssertEqualObjects([_converter processLine: @"=> 	 f<oo.PNG"],
	    @"<img class='gemtext-image' src='f&lt;oo.PNG'/><br/>");

	OTAssertEqualObjects(
	    [_converter processLine: @"=> 	 f<oo.png 	 "],
	    @"<img class='gemtext-image' src='f&lt;oo.png'/><br/>");

	OTAssertEqualObjects([_converter finishProcessing], @"");
}

- (void)testExternalImageLink
{
	OTAssertEqualObjects([_converter processLine:
	    @"=>gemini://x/f<oo.png"],
	    @"<img class='gemtext-image' src='gemini://x/f&lt;oo.png'/><br/>");

	OTAssertEqualObjects([_converter processLine:
	    @"=> gemini://x/f<oo.png?asd"],
	    @"<img class='gemtext-image' src='gemini://x/f&lt;oo.png?asd'/>"
	    @"<br/>");

	OTAssertEqualObjects([_converter processLine:
	    @"=> 	 gemini://x/f<oo.PNG"],
	    @"<img class='gemtext-image' src='gemini://x/f&lt;oo.PNG'/><br/>");

	OTAssertEqualObjects([_converter processLine:
	    @"=> 	 gemini://x/f<oo.png 	 "],
	    @"<img class='gemtext-image' src='gemini://x/f&lt;oo.png'/><br/>");

	OTAssertEqualObjects([_converter finishProcessing], @"");
}

- (void)testLocalImageLinkWithDescription
{
	OTAssertEqualObjects([_converter processLine: @"=>f<oo.png b<ar"],
	    @"<img class='gemtext-image' src='f&lt;oo.png' alt='b&lt;ar'/>"
	    @"<br/>");

	OTAssertEqualObjects([_converter processLine: @"=> f<oo.png?asd b<ar"],
	    @"<img class='gemtext-image' src='f&lt;oo.png?asd' alt='b&lt;ar'/>"
	    @"<br/>");

	OTAssertEqualObjects(
	    [_converter processLine: @"=> 	 f<oo.PNG 	 b<ar"],
	    @"<img class='gemtext-image' src='f&lt;oo.PNG' alt='b&lt;ar'/>"
	    @"<br/>");

	OTAssertEqualObjects(
	    [_converter processLine: @"=> 	 f<oo.png 	 b<ar 	 "],
	    @"<img class='gemtext-image' src='f&lt;oo.png' "
	    @"alt='b&lt;ar 	 '/><br/>");

	OTAssertEqualObjects([_converter finishProcessing], @"");
}

- (void)testExternalImageLinkWithDescription
{
	OTAssertEqualObjects([_converter processLine:
	    @"=>gemini://x/f<oo.png b<ar"],
	    @"<img class='gemtext-image' src='gemini://x/f&lt;oo.png' "
	    @"alt='b&lt;ar'/><br/>");

	OTAssertEqualObjects([_converter processLine:
	    @"=> 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/>");

	OTAssertEqualObjects([_converter processLine:
	    @"=> 	 gemini://x/f<oo.PNG 	 b<ar"],
	    @"<img class='gemtext-image' src='gemini://x/f&lt;oo.PNG' "
	    @"alt='b&lt;ar'/><br/>");

	OTAssertEqualObjects([_converter processLine:
	    @"=> 	 gemini://x/f<oo.png 	 b<ar 	 "],
	    @"<img class='gemtext-image' src='gemini://x/f&lt;oo.png' "
	    @"alt='b&lt;ar 	 '/><br/>");

	OTAssertEqualObjects([_converter finishProcessing], @"");
}

- (void)testInvalidLinkThrows
{
	OTAssertThrowsSpecific([_converter processLine: @"=>"],
	    OFInvalidFormatException);

	OTAssertThrowsSpecific([_converter processLine: @"=> 		 "],
	    OFInvalidFormatException);
}

- (void)testList
{
	OTAssertEqualObjects([_converter processLine: @"Foo"], @"Foo<br/>");
	OTAssertEqualObjects([_converter processLine: @"* li<1"],
	    @"<ul class='gemtext-list' style='margin-top: 0; margin-bottom: 0'>"
	    @"\n"
	    @"<li class='gemtext-list-item'>li&lt;1</li>");
	OTAssertEqualObjects([_converter processLine: @"* li<2"],
	    @"<li class='gemtext-list-item'>li&lt;2</li>");
	OTAssertEqualObjects([_converter processLine: @"Bar"],
	    @"</ul>\n"
	    @"Bar<br/>");

	OTAssertEqualObjects([_converter processLine: @"* li<3"],
	    @"<ul class='gemtext-list' style='margin-top: 0; margin-bottom: 0'>"
	    @"\n"
	    @"<li class='gemtext-list-item'>li&lt;3</li>");
	OTAssertEqualObjects([_converter finishProcessing], @"</ul>");
}

- (void)testQuote
{
	OTAssertEqualObjects([_converter processLine: @"Foo"], @"Foo<br/>");
	OTAssertEqualObjects([_converter processLine: @">q<1"],
	    @"<blockquote class='gemtext-quote' style='margin-top: 0; "
	    @"margin-bottom: 0'>\n"
	    @"q&lt;1<br/>");
	OTAssertEqualObjects([_converter processLine: @"> q<2"],
	    @" q&lt;2<br/>");
	OTAssertEqualObjects([_converter processLine: @"Bar"],
	    @"</blockquote>\n"
	    @"Bar<br/>");

	OTAssertEqualObjects([_converter processLine: @">q<3"],
	    @"<blockquote class='gemtext-quote' style='margin-top: 0; "
	    @"margin-bottom: 0'>\n"
	    @"q&lt;3<br/>");
	OTAssertEqualObjects([_converter finishProcessing], @"</blockquote>");
}

- (void)testPreformatted
{
	OTAssertEqualObjects([_converter processLine: @"Foo"], @"Foo<br/>");
	OTAssertEqualObjects([_converter processLine: @"```"],
	    @"<pre class='gemtext-preformatted' style='margin-top: 0; "
	    @"margin-bottom: 0'>");
	OTAssertEqualObjects([_converter processLine: @" B<ar"], @" B&lt;ar");
	OTAssertEqualObjects([_converter processLine: @"B<az"], @"B&lt;az");
	OTAssertEqualObjects([_converter processLine: @"```"], @"</pre>");
	OTAssertEqualObjects([_converter processLine: @"Qux"], @"Qux<br/>");
	OTAssertEqualObjects([_converter finishProcessing], @"");
}

- (void)testUnclosedPreformattedThrows
{
	OTAssertEqualObjects([_converter processLine: @"```"],
	    @"<pre class='gemtext-preformatted' style='margin-top: 0; "
	    @"margin-bottom: 0'>");
	OTAssertThrowsSpecific([_converter finishProcessing],
	    OFInvalidFormatException);
}
@end
