package org.xbib.logging.log4j.test;

import java.net.URI;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.spi.LoggerContext;
import org.apache.logging.log4j.status.StatusListener;
import org.apache.logging.log4j.status.StatusLogger;
import org.xbib.logging.Level;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.xbib.logging.log4j.XbibStatusListener;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

public class StatusLoggerTest extends AbstractTest {

    private TestQueueHandler handler;

    private org.xbib.logging.Logger lmLogger;

    private StatusLogger statusLogger;

    @BeforeEach
    public void setup() {
        lmLogger = org.xbib.logging.Logger.getLogger("org.xbib.logging.log4j.status");
        final TestQueueHandler handler = new TestQueueHandler();
        lmLogger.addHandler(handler);
        this.handler = handler;
        // Required to initialize the XbibStatusListener
        LogManager.getContext();
        statusLogger = StatusLogger.getLogger();
        lmLogger.setLevel(Level.WARN);
    }

    @AfterEach
    public void tearDown() {
        handler.close();
        lmLogger.removeHandler(handler);
    }

    @Test
    public void testListenerAttached() {
        boolean found = false;
        for (StatusListener listener : statusLogger.getListeners()) {
            if (listener.getClass().equals(XbibStatusListener.class)) {
                found = true;
                break;
            }
        }
        assertTrue(found,
                "Expected to find " + XbibStatusListener.class.getName() + " registered: " + statusLogger.getListeners());
    }

    @Test
    public void testError() {
        // Log an error which should show up on the handler
        statusLogger.error("Test status message");
        checkEmpty(false);
        final String msg = handler.pollFirstFormatted();
        assertNotNull(msg);
        assertEquals("Test status message", msg);
    }

    @Test
    public void testLevelChange() {
        // Log a warning message which should be ignored
        statusLogger.info("Test info message 1");
        checkEmpty(true);

        // Set the level to warn and log another message
        lmLogger.setLevel(Level.INFO);
        statusLogger.info("Test info message 2");
        checkEmpty(false);
        assertEquals("Test info message 2", handler.pollFormatted());
    }

    @Test
    public void testConfiguration() throws Exception {
        final URI config = LoggerContextTest.class.getResource("/log4j2.xml").toURI();
        final LoggerContext loggerContext = LogManager.getContext(LoggerContextTest.class.getClassLoader(), true, config);
        assertNotNull(loggerContext);
        // The status logger should contain a message
        checkEmpty(false);
        final String foundMsg = handler.pollFirstFormatted();
        assertTrue(foundMsg.contains(config.toString()),
                String.format("Expected the log message to contain %s. Found %s", config, foundMsg));
    }

    private void checkEmpty(final boolean expectEmpty) {
        if (handler.isEmpty() != expectEmpty) {
            final StringBuilder msg = new StringBuilder("Expect the data to ");
            if (expectEmpty) {
                msg.append("be empty, found:")
                        .append(System.lineSeparator());
                String logMsg;
                while ((logMsg = handler.pollFirstFormatted()) != null) {
                    msg.append(logMsg).append(System.lineSeparator());
                }
            } else {
                msg.append("not be empty");
            }
            fail(msg.toString());
        }
    }
}
