package org.xbib.logging.integration;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

@Disabled
public class SystemLoggerIT {

    private Path stdout;
    private Path logFile;

    @BeforeEach
    public void setup() throws Exception {
        stdout = Files.createTempFile("stdout", ".txt");
        logFile = Files.createTempFile("system-logger", ".log");
    }

    @AfterEach
    public void killProcess() throws IOException {
        Files.deleteIfExists(stdout);
        Files.deleteIfExists(logFile);
    }

    @Test
    public void testSystemLoggerActivated() throws Exception {
        final Process process = createProcess();
        if (process.waitFor(3L, TimeUnit.SECONDS)) {
            final int exitCode = process.exitValue();
            final StringBuilder msg = new StringBuilder("Expected exit value 0 got ")
                    .append(exitCode);
            appendStdout(msg);
            Assertions.assertEquals(0, exitCode, msg.toString());
        } else {
            final Process destroyed = process.destroyForcibly();
            final StringBuilder msg = new StringBuilder("Failed to exit process within 3 seconds. Exit Code: ")
                    .append(destroyed.exitValue());
            appendStdout(msg);
            Assertions.fail(msg.toString());
        }

        /*
        final JsonObject json = readLogFile(logFile);
        final JsonArray lines = json.getJsonArray("lines");
        Assertions.assertEquals(2, lines.size());
        // The first line should be from a SystemLogger
        JsonObject line = lines.getJsonObject(0);
        Assertions.assertEquals("org.xbib.logging.LoggerFinder$SystemLogger", line.getString("loggerClassName"));
        JsonObject mdc = line.getJsonObject("mdc");
        Assertions.assertEquals("org.xbib.logging.LoggerFinder$SystemLogger", mdc.getString("logger.type"));
        Assertions.assertEquals(LogManager.class.getName(), mdc.getString("java.util.logging.LogManager"));
        Assertions.assertEquals(LogManager.class.getName(), mdc.getString("java.util.logging.manager"));


        // The second line should be from a JUL logger
        line = lines.getJsonObject(1);
        Assertions.assertEquals(Logger.class.getName(), line.getString("loggerClassName"));
        mdc = line.getJsonObject("mdc");
        Assertions.assertEquals(Logger.class.getName(), mdc.getString("logger.type"));
        Assertions.assertEquals(LogManager.class.getName(), mdc.getString("java.util.logging.LogManager"));
        Assertions.assertEquals(LogManager.class.getName(), mdc.getString("java.util.logging.manager"));
         */
    }

    @Test
    public void testSystemLoggerAccessedBeforeActivated() throws Exception {
        final Process process = createProcess("-Dsystem.logger.test.jul=true",
                "-Djava.util.logging.config.class=" + TestLogContextConfigurator.class.getName());
        if (process.waitFor(3L, TimeUnit.SECONDS)) {
            final int exitCode = process.exitValue();
            final StringBuilder msg = new StringBuilder("Expected exit value 0 got ")
                    .append(exitCode);
            appendStdout(msg);
            Assertions.assertEquals(0, exitCode, msg.toString());
        } else {
            final Process destroyed = process.destroyForcibly();
            final StringBuilder msg = new StringBuilder("Failed to exit process within 3 seconds. Exit Code: ")
                    .append(destroyed.exitValue());
            appendStdout(msg);
            Assertions.fail(msg.toString());
        }

        /*
        final JsonObject json = readLogFile(logFile);
        final JsonArray lines = json.getJsonArray("lines");
        Assertions.assertEquals(3, lines.size());

        // The first line should be an error indicating the java.util.logging.manager wasn't set before the LogManager
        // was accessed
        JsonObject line = lines.getJsonObject(0);
        Assertions.assertEquals("ERROR", line.getString("level"));
        final String message = line.getString("message");
        Assertions.assertNotNull(message);
        Assertions.assertTrue(message.contains("java.util.logging.manager"));

        // The second line should be from a SystemLogger
        line = lines.getJsonObject(1);
        Assertions.assertEquals("org.xbib.logging.LoggerFinder$SystemLogger", line.getString("loggerClassName"));
        JsonObject mdc = line.getJsonObject("mdc");
        Assertions.assertEquals("org.xbib.logging.LoggerFinder$SystemLogger", mdc.getString("logger.type"));
        Assertions.assertEquals("java.util.logging.LogManager", mdc.getString("java.util.logging.LogManager"));

        // The third line should be from a JUL logger
        line = lines.getJsonObject(2);
        Assertions.assertEquals("java.util.logging.Logger", line.getString("loggerClassName"));
        mdc = line.getJsonObject("mdc");
        Assertions.assertEquals("java.util.logging.Logger", mdc.getString("logger.type"));
        Assertions.assertEquals("java.util.logging.LogManager", mdc.getString("java.util.logging.LogManager"));
         */
    }

    private Process createProcess(final String... javaOpts) throws IOException {
        final List<String> cmd = new ArrayList<>();
        cmd.add(findJavaCommand());
        cmd.add("--add-modules=ALL-MODULE-PATH");
        cmd.add("-Dorg.xbib.logging.test.configure=true");
        cmd.add("-Dtest.log.file.name=" + logFile.toString());
        Collections.addAll(cmd, javaOpts);
        cmd.add("-cp");
        cmd.add(System.getProperty("java.class.path")); // does not work in Gradle!
        cmd.add(SystemLoggerMain.class.getName());
        System.out.println(cmd);
        return new ProcessBuilder(cmd)
                .redirectErrorStream(true)
                .redirectOutput(stdout.toFile())
                .start();
    }

    private void appendStdout(final StringBuilder builder) throws IOException {
        for (String line : Files.readAllLines(stdout)) {
            builder.append(System.lineSeparator())
                    .append(line);
        }
    }

    /*private static JsonObject readLogFile(final Path logFile) throws IOException {
        final JsonArrayBuilder builder = Json.createArrayBuilder();
        try (BufferedReader reader = Files.newBufferedReader(logFile, StandardCharsets.UTF_8)) {
            String line;
            while ((line = reader.readLine()) != null) {
                try (JsonReader jsonReader = Json.createReader(new StringReader(line))) {
                    builder.add(jsonReader.read());
                }
            }
        }
        return Json.createObjectBuilder().add("lines", builder).build();
    }*/

    // this hack does not respect Gradle toolchain
    private static String findJavaCommand() {
        String javaHome = System.getProperty("java.home");
        if (javaHome != null) {
            return Paths.get(javaHome, "bin", "java").toAbsolutePath().toString();
        }
        return "java";
    }
}
