{#- Java test file template for JUnit 5

   Context variables:
   - header: file header comment
   - java_group_id: Java package ID (e.g., "com.example.e2e")
   - test_class_name: test class name (e.g., "BasicTest")
   - category: category name from fixture group
   - imports: list of import strings (pre-formatted)
   - needs_object_mapper: boolean
   - fixtures_body: string containing all rendered test methods
   - uses_harness: boolean, true if server-pattern harness is active
#}
{{ header }}
package {{ java_group_id }}.e2e;

{% if imports %}
{% for imp in imports %}
{{ imp }}
{% endfor %}
{% endif %}
{%- if uses_harness %}
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.AfterAll;
import java.io.IOException;
import java.net.Socket;
import java.net.URI;
import java.time.Instant;
{%- endif %}

/** E2e tests for category: {{ category }}. */
class {{ test_class_name }} {
{%- if needs_object_mapper %}

    private static final ObjectMapper MAPPER = new ObjectMapper().registerModule(new Jdk8Module()).setPropertyNamingStrategy(com.fasterxml.jackson.databind.PropertyNamingStrategies.SNAKE_CASE);
{%- endif %}
{%- if uses_harness %}

    private static Process harnessProcess;
    private static String sutUrl;

    @BeforeAll
    static void startHarness() throws Exception {
        String preset = System.getenv("SUT_URL");
        if (preset != null && !preset.isEmpty()) {
            sutUrl = preset;
            return;
        }

        String javaHome = System.getProperty("java.home");
        ProcessBuilder pb = new ProcessBuilder(
            javaHome + "/bin/java",
            "-cp", System.getProperty("java.class.path"),
            "{{ java_group_id }}.e2e.HarnessMain"
        );
        pb.redirectErrorStream(false);
        harnessProcess = pb.start();

        // Read SUT_URL from harness stdout
        java.io.BufferedReader reader = new java.io.BufferedReader(
            new java.io.InputStreamReader(harnessProcess.getInputStream(), "UTF-8")
        );
        String line;
        long deadline = System.currentTimeMillis() + 15_000;
        while (System.currentTimeMillis() < deadline && (line = reader.readLine()) != null) {
            if (line.startsWith("SUT_URL=")) {
                sutUrl = line.substring("SUT_URL=".length()).trim();
                break;
            }
        }

        if (sutUrl == null || sutUrl.isEmpty()) {
            if (harnessProcess != null) {
                harnessProcess.destroyForcibly();
            }
            throw new RuntimeException("Harness did not emit SUT_URL within 15s");
        }

        // TCP-readiness probe: ensure harness is accepting connections
        java.net.URI uri = new java.net.URI(sutUrl);
        String host = uri.getHost();
        int port = uri.getPort() > 0 ? uri.getPort() : 8000;
        deadline = System.currentTimeMillis() + 15_000;
        boolean ready = false;
        while (System.currentTimeMillis() < deadline) {
            if (harnessProcess.isAlive() == false) {
                break;
            }
            try (Socket socket = new Socket(host, port)) {
                ready = true;
                break;
            } catch (IOException e) {
                Thread.sleep(100);
            }
        }

        if (!ready) {
            if (harnessProcess != null) {
                harnessProcess.destroyForcibly();
            }
            throw new RuntimeException("Harness did not become reachable at " + sutUrl + " within 15s");
        }

        System.setProperty("SUT_URL", sutUrl);
    }

    @AfterAll
    static void stopHarness() throws Exception {
        if (harnessProcess != null && harnessProcess.isAlive()) {
            harnessProcess.destroy();
            if (!harnessProcess.waitFor(5, java.util.concurrent.TimeUnit.SECONDS)) {
                harnessProcess.destroyForcibly();
            }
        }
    }
{%- endif %}

{{ fixtures_body }}
}
