/*
 * Copyright 2021-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.opentest4j.reporting.events.api;

import org.intellij.lang.annotations.Language;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.opentest4j.reporting.schema.Namespace;
import org.opentest4j.reporting.schema.QualifiedName;

import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;

import static org.xmlunit.assertj3.XmlAssert.assertThat;

class DocumentWriterTest {

	static final Namespace NAMESPACE_A = Namespace.of("urn::a");
	static final Namespace NAMESPACE_B = Namespace.of("urn::b");
	static final NamespaceRegistry NAMESPACE_REGISTRY = NamespaceRegistry.builder(NAMESPACE_A) //
			.add("b", NAMESPACE_B, "https://example.org/b.xsd") //
			.build();

	@TempDir
	Path tempDir;

	@Test
	void producesWellFormedNamespacedXml() throws Exception {
		var targetFile = tempDir.resolve("test.xml");

		try (var writer = DocumentWriter.<Root> create(Root.ELEMENT, NAMESPACE_REGISTRY, targetFile)) {
			writer.append(Child::new, child -> child //
					.withAttribute(QualifiedName.of(NAMESPACE_A, "a"), "1") //
					.withAttribute(QualifiedName.of(NAMESPACE_B, "b"), "2\r\n\t&< 3 >\0") //
					.append(Extension::new, extension -> extension.withContent("con<t>ent\1")));
		}

		@Language("xml")
		var expected = """
				<root xmlns="urn::a" xmlns:b="urn::b"
				      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
				      xsi:schemaLocation="urn::b https://example.org/b.xsd">
				    <child a="1" b:b="2&#13;&#10;&#9;&amp;&lt; 3 &gt;�">
				        <b:extension>con&lt;t&gt;ent�</b:extension>
				    </child>
				</root>
				""";
		assertThat(targetFile).and(expected).ignoreWhitespace().areIdentical();
		assertThat(targetFile).withNamespaceContext(Map.of("a", "urn::a", "b", "urn::b")) //
				.valueByXPath("//a:child/@b:b").isEqualTo("2\r\n\t&< 3 >\uFFFD");

	}

	@Test
	void escapesNestedCDataSections() throws Exception {
		var targetFile = tempDir.resolve("test.xml");

		var message = "<foo><![CDATA[bar]]></foo>";

		try (var writer = DocumentWriter.<Root> create(Root.ELEMENT, NamespaceRegistry.builder(NAMESPACE_A).build(),
			targetFile)) {
			writer.append(Child::new, child -> child //
					.withAttribute(QualifiedName.of(NAMESPACE_A, "a"), message) //
					.withCDataSection(message));
		}

		assertThat(targetFile).withNamespaceContext(Map.of("a", "urn::a")) //
				.valueByXPath("//a:child").isEqualTo(message);
		assertThat(targetFile).withNamespaceContext(Map.of("a", "urn::a")) //
				.valueByXPath("//a:child/@a").isEqualTo(message);
	}

	@Test
	void producesWellFormedNamespacedXmlWithWriter() throws Exception {
		var stringWriter = new StringWriter();

		try (var writer = DocumentWriter.<Root> create(Root.ELEMENT, NAMESPACE_REGISTRY, stringWriter)) {
			writer.append(Child::new, child -> child //
					.withAttribute(QualifiedName.of(NAMESPACE_A, "a"), "1") //
					.withAttribute(QualifiedName.of(NAMESPACE_B, "b"), "2\r\n\t&< 3 >\0") //
					.append(Extension::new, extension -> extension.withContent("con<t>ent\1")));
		}

		@Language("xml")
		var expected = """
				<root xmlns="urn::a" xmlns:b="urn::b"
				      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
				      xsi:schemaLocation="urn::b https://example.org/b.xsd">
				    <child a="1" b:b="2&#13;&#10;&#9;&amp;&lt; 3 &gt;�">
				        <b:extension>con&lt;t&gt;ent�</b:extension>
				    </child>
				</root>
				""";
		assertThat(stringWriter.toString()).and(expected).ignoreWhitespace().areIdentical();
		assertThat(stringWriter.toString()).withNamespaceContext(Map.of("a", "urn::a", "b", "urn::b")) //
				.valueByXPath("//a:child/@b:b").isEqualTo("2\r\n\t&< 3 >\uFFFD");
	}

	static class Root extends Element<Root> {
		static final QualifiedName ELEMENT = QualifiedName.of(NAMESPACE_A, "root");

		Root(Context context) {
			super(context, ELEMENT);
		}
	}

	static class Child extends ChildElement<Root, Child> {
		Child(Context context) {
			super(context, QualifiedName.of(NAMESPACE_A, "child"));
		}
	}

	static class Extension extends ChildElement<Child, Extension> {
		Extension(Context context) {
			super(context, QualifiedName.of(NAMESPACE_B, "extension"));
		}
	}
}
