package oving6.logger;

import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.ByteArrayOutputStream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class DistributingLoggerTest {

	private static final String formatString = "%s: %s (%s)";

	private ByteArrayOutputStream infoStream;
	private ByteArrayOutputStream warnStream;
	private ByteArrayOutputStream errorStream;
	private DistributingLogger logger;
	private StreamLogger infoLogger;
	private StreamLogger warnLogger;
	private StreamLogger errorLogger;

	@BeforeEach
	public void setUp() {
		infoStream = new ByteArrayOutputStream();
		warnStream = new ByteArrayOutputStream();
		errorStream = new ByteArrayOutputStream();

		infoLogger = new StreamLogger(infoStream);
		warnLogger = new StreamLogger(warnStream);
		errorLogger = new StreamLogger(errorStream);

		logger = new DistributingLogger(errorLogger, warnLogger, infoLogger);
	}

	@Test
	@DisplayName("Log to INFO")
	public void testLogToInfo() {
		infoLogger.setFormatString(formatString);
		logger.log(ILogger.INFO, "Dette er en info-melding.", null);
		assertEquals(String.format(formatString, ILogger.INFO, "Dette er en info-melding.", null),
				infoStream.toString().trim(), "Test the format of the info message after logging");
		assertEquals("", warnStream.toString(),
				"Test the warning stream after logging an info message");
		assertEquals("", errorStream.toString(),
				"Test the error stream after logging an info message");
	}

	@Test
	@DisplayName("Log to WARNING")
	public void testLogToWarn() {
		warnLogger.setFormatString(formatString);
		logger.log(ILogger.WARNING, "Dette er en advarsel.", null);
		assertEquals(String.format(formatString, ILogger.WARNING, "Dette er en advarsel.", null),
				warnStream.toString().trim(),
				"Test the format of the warning message after logging");
		assertEquals("", infoStream.toString(),
				"Test the info stream after logging a warning message");
		assertEquals("", errorStream.toString(),
				"Test the error stream after logging a warning message");
	}

	@Test
	@DisplayName("Log to ERROR")
	public void testLogToError() {
		Exception exception = new IllegalStateException();
		errorLogger.setFormatString(formatString);
		logger.log(ILogger.ERROR, "Dette er en feilmelding.", exception);
		assertEquals(
				String.format(formatString, ILogger.ERROR, "Dette er en feilmelding.", exception),
				errorStream.toString().trim(),
				"Test the format of the error message after logging");
		assertEquals("", warnStream.toString(),
				"Test the warning stream after logging an error message");
		assertEquals("", infoStream.toString(),
				"Test the info stream after logging an error message");
	}

	@Test
	@DisplayName("Change info logger")
	public void testChangeInfoLogger() {
		ByteArrayOutputStream newInfoStream = new ByteArrayOutputStream();
		StreamLogger newInfoLogger = new StreamLogger(newInfoStream);

		infoLogger.setFormatString(formatString);
		logger.log(ILogger.INFO, "Dette er en info-melding.", null);
		assertEquals(String.format(formatString, ILogger.INFO, "Dette er en info-melding.", null),
				infoStream.toString().trim(), "Test the format of the info message after logging");

		newInfoLogger.setFormatString(formatString);
		logger.setLogger(ILogger.INFO, newInfoLogger);
		logger.log(ILogger.INFO, "Dette er den andre info-meldingen.", null);
		assertEquals(String.format(formatString, ILogger.INFO, "Dette er en info-melding.", null),
				infoStream.toString().trim(),
				"Test the first info stream after switching the info logger and logging another info message");
		assertEquals(
				String.format(formatString, ILogger.INFO, "Dette er den andre info-meldingen.",
						null),
				newInfoStream.toString().trim(),
				"Test the second info stream after switching the info logger and logging a new info message");
	}
}
