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

import {
  LoggerConfig,
  LoggerLevel,
  LoggerLevelName,
  _LOGGER_CONFIG,
} from '../core';

export type ConsoleFunc = (message?: any, ...optionalParams: any[]) => void;

@Injectable()
export class LoggerService {
  private _isIE: boolean;

  public constructor(
    @Inject(_LOGGER_CONFIG) private readonly config: LoggerConfig,
    @Inject(PLATFORM_ID) private readonly platformId,
  ) {
    this._isIE = isPlatformBrowser(platformId) &&
    !!(navigator.userAgent.indexOf('MSIE') !== -1 || navigator.userAgent.match(/Trident\//) || navigator.userAgent.match(/Edge\//));

  }

  public get trace(): ConsoleFunc {
    return this.getConsoleMethod(LoggerLevel.TRACE);
  }
  public get debug(): ConsoleFunc {
    return this.getConsoleMethod(LoggerLevel.DEBUG);
  }
  public get info(): ConsoleFunc {
    return this.getConsoleMethod(LoggerLevel.INFO);
  }
  public get log(): ConsoleFunc {
    return this.getConsoleMethod(LoggerLevel.LOG);
  }
  public get warn(): ConsoleFunc {
    return this.getConsoleMethod(LoggerLevel.WARN);
  }
  public get error(): ConsoleFunc {
    return this.getConsoleMethod(LoggerLevel.ERROR);
  }

  private _console_log: ConsoleFunc;

  private getConsoleMethod(level: LoggerLevel): ConsoleFunc {
    if (level < this.config.level) {
      return (message, ...additional: any[]) => {};
    }

    if (this._isIE) {
      switch (level) {
        case LoggerLevel.WARN:
          return console.warn.bind(console, ...this.getLogHeader(level));
        case LoggerLevel.ERROR:
          return console.error.bind(console, ...this.getLogHeader(level));
        case LoggerLevel.INFO:
          return console.info.bind(console, ...this.getLogHeader(level));
        default:
          return console.log.bind(console, ...this.getLogHeader(level));
      }
    } else {
      return console.log.bind(console, ...this.getLogHeader(level));
    }
  }

  private getLogHeader(level: LoggerLevel): any[] {
    const params: any[] = [];
    params.push(`%c${this._timestamp()} [${LoggerLevelName[level]}]`);
    if (!this._isIE) {
      const color = this._getColor(level);
      params.push(`color:${color}`);
    }
    return params;
  }

  private _timestamp(): string {
    return new Date().toISOString();
  }

  private _log(level: LoggerLevel, message, additional: any[] = []): void {
    if (!message) {
      return;
    }

    // Allow logging on server even if client log level is off
    // if (logOnServer) {
    //   this._logOnServer(level, message, additional);
    // }

    // if no message or the log level is less than the environ
    if (level < this.config.level) {
      return;
    }

    try {
      message = typeof message === 'string' ? message : JSON.stringify(message, null, 2);
    } catch (e) {
      additional = [message, ...additional];
      message = 'The provided "message" value could not be parsed with JSON.stringify().';
    }

    // Coloring doesn't work in IE
    if (this._isIE) {
      return this._logIE(level, message, additional);
    }

    const color = this._getColor(level);

    const params: any[] = [];
    params.push(`%c${this._timestamp()} [${LoggerLevelName[level]}]`);
    params.push(`color:${color}`);
    params.push(message);
    params.push(...additional);

    console.log.apply(console, params);
  }

  private _logIE(level: LoggerLevel, message: string, additional: any[] = []): void {
    const params: any[] = [];
    params.push(`${this._timestamp()} [${LoggerLevelName[level]}] `);
    params.push(message);
    params.push(...additional);

    switch (level) {
      case LoggerLevel.WARN:
        console.warn.apply(console, params);
        break;
      case LoggerLevel.ERROR:
        console.error.apply(console, params);
        break;
      case LoggerLevel.INFO:
        console.info.apply(console, params);
        break;
      default:
        console.log.apply(console, params);
    }
  }

  private _getColor(level: LoggerLevel): 'blue' | 'teal' | 'gray' | 'red' | undefined {
    switch (level) {
      case LoggerLevel.TRACE:
        return 'blue';
      case LoggerLevel.DEBUG:
        return 'teal';
      case LoggerLevel.INFO:
      case LoggerLevel.LOG:
        return 'gray';
      case LoggerLevel.WARN:
      case LoggerLevel.ERROR:
        return 'red';
      case LoggerLevel.OFF:
      default:
        return;
    }
  }
}
