import { Inject, Injectable } from '@angular/core';

export enum LogLevel {
  Off = 0,
  Error,
  Warning,
  Info,
  Debug,
  Trace,
}

/* eslint-disable  @typescript-eslint/no-explicit-any */
export type LogOutput = (
  source: string,
  level: LogLevel,
  ...objects: any[]
) => void;

@Injectable({
  providedIn: 'root',
})
export class Logger {
  static level = LogLevel.Info;

  static outputs: LogOutput[] = [];

  static enableProductionMode() {
    Logger.level = LogLevel.Warning;
  }

  static enableDevMode(withTrace: boolean = false) {
    Logger.level = withTrace ? LogLevel.Trace : LogLevel.Debug;
  }

  constructor(@Inject(String) private source: string) {}

  trace(...objects: any[]) {
    this.log(console.log, LogLevel.Trace, objects);
  }

  debug(...objects: any[]) {
    this.log(console.log, LogLevel.Debug, objects);
  }

  info(...objects: any[]) {
    this.log(console.info, LogLevel.Info, objects);
  }

  warn(...objects: any[]) {
    this.log(console.warn, LogLevel.Warning, objects);
  }

  error(...objects: any[]) {
    this.log(console.error, LogLevel.Error, objects);
  }

  private log(func: any, level: LogLevel, objects: any[]) {
    if (level <= Logger.level) {
      const log = this.source
        ? [`[${LogLevel[level]}]`, `[${this.source}]`].concat(objects)
        : objects;
      func.apply(console, log);
      Logger.outputs.forEach(output =>
        output.apply(output, [this.source, level, ...objects])
      );
    }
  }
}
