export enum LogLevel {
	DEBUG = "DEBUG",
	INFO = "INFO",
	WARNING = "WARNING",
	ERROR = "ERROR"
}

const LOG_LEVEL_ORDER = {
	[LogLevel.DEBUG]: 0,
	[LogLevel.INFO]: 1,
	[LogLevel.WARNING]: 2,
	[LogLevel.ERROR]: 3
};

export class LogLine {
	public timestamp = new Date();
	public constructor(
		public level: LogLevel,
		public message: string
	) {}
}

export class Logger {
	private static readonly MAX_MESSAGES = 100000;
	private readonly messages: LogLine[] = [];
	private readonly onMessageListeners: ((message: LogLine) => unknown)[] = [];

	public constructor(
		...onMessageListeners: ((message: LogLine) => unknown)[]
	) {
		this.onMessageListeners = onMessageListeners;
	}

	public debug(message: string): void {
		this.pushMessage(message, LogLevel.DEBUG);
	}

	public info(message: string): void {
		this.pushMessage(message, LogLevel.INFO);
	}

	public warn(message: string): void {
		this.pushMessage(message, LogLevel.WARNING);
	}

	public error(message: string): void {
		this.pushMessage(message, LogLevel.ERROR);
	}

	public getMessages(mininumSeverity: LogLevel): LogLine[] {
		return this.messages.filter(
			(message) =>
				LOG_LEVEL_ORDER[message.level] >=
				LOG_LEVEL_ORDER[mininumSeverity]
		);
	}

	public addOnMessageListener(listener: (message: LogLine) => unknown): void {
		this.onMessageListeners.push(listener);
	}

	public reset(): void {
		this.messages.length = 0;
		this.debug("Logger has been reset");
	}

	private pushMessage(message: string, level: LogLevel): void {
		const logLine = new LogLine(level, message);
		this.messages.push(logLine);

		while (this.messages.length > Logger.MAX_MESSAGES) {
			this.messages.shift();
		}

		this.onMessageListeners.forEach((listener) => {
			listener(logLine);
		});
	}
}
