/*
 * log.ts
 *
 * Copyright (C) 2022 by Posit Software, PBC
 *
 * Unless you have received this program directly from Posit Software pursuant
 * to the terms of a commercial license agreement with Posit Software, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

import { coreState } from './core-state';

export interface Logger {
  logLevel(): string;
  setLogLevel(level: string): void;
  logError(err: unknown): void;
  logErrorAtLevel(level: string, err: unknown): void;
  logErrorMessage(message: string): void;
  logWarning(warning: string): void;
  logInfo(message: string): void;
  logDebug(message: string): void;
  logDiagnostic(message: string): void;
  logDiagnosticEnvVar(name: string): void;
  closeLogFile(): void;
  ensureTransport(): void;
}

export interface LogOptions {
  logger: Logger;
  showDiagnostics: boolean; // special class of console logging enabled via '--run-diagnostics'
}

export class NullLogger implements Logger {
  level = 'error';

  logLevel(): string {
    return this.level;
  }
  setLogLevel(level: string): void {
    this.level = level;
  }
  logError(err: unknown): void {
    this.logErrorAtLevel('error', err);
  }
  logErrorAtLevel(_level: string, _err: unknown): void {
    /**/
  }
  logErrorMessage(_message: string): void {
    /**/
  }
  logInfo(_message: string): void {
    /**/
  }
  logWarning(_warning: string): void {
    /**/
  }
  logDebug(_message: string): void {
    /**/
  }
  logDiagnostic(_message: string): void {
    /**/
  }
  logDiagnosticEnvVar(_name: string): void {
    /**/
  }
  closeLogFile(): void {
    /**/
  }
  ensureTransport(): void {
    /**/
  }
}

/**
 * @returns Global logger instance
 */
export function logger(): Logger {
  return coreState().logOptions.logger;
}

/**
 * @returns Current logging level
 */
export function logLevel(): string {
  return coreState().logOptions.logger.logLevel();
}

export function setLogger(logger: Logger): void {
  coreState().logOptions.logger = logger;
}

export function enableDiagnosticsOutput(): void {
  coreState().logOptions.showDiagnostics = true;
}

export function showDiagnosticsOutput(): boolean {
  return coreState().logOptions.showDiagnostics;
}

/**
 * @param level minimum logging level
 */
export function setLoggerLevel(level: string): void {
  coreState().logOptions.logger.setLogLevel(level);
}

/**
 * Convert a command-line log level string (e.g. 'WARN' or 'WARNING') to internal log level.
 * Returns 'trace' for TRACE even though Winston doesn't support it - we'll map it later.
 *
 * @param level Command-line string representation of log level
 * @param defaultLevel Default logging level if unable to parse input
 * @returns Log level string (error, warn, info, debug, trace)
 */
export function parseCommandLineLogLevel(level: string, defaultLevel: string): string {
  level = level.toUpperCase();
  switch (level) {
    case 'ERR':
    case 'ERROR':
      return 'error';
    case 'WARN':
    case 'WARNING':
      return 'warn';
    case 'INFO':
      return 'info';
    case 'DEBUG':
      return 'debug';
    case 'TRACE':
      return 'trace';
    default:
      return defaultLevel;
  }
}

/**
 * Convert internal log level to C++ Logger level for rsession.
 * C++ Logger expects: ERROR, WARNING, INFO, DEBUG, TRACE, OFF
 *
 * @param level Internal log level (error, warn, info, debug, trace, etc.)
 * @returns C++ Logger level string (ERROR, WARNING, INFO, DEBUG, TRACE)
 */
export function levelToCppLevel(level: string): string {
  switch (level.toLowerCase()) {
    case 'error':
      return 'ERROR';
    case 'warn':
      return 'WARNING';
    case 'info':
      return 'INFO';
    case 'debug':
      return 'DEBUG';
    case 'trace':
      return 'TRACE';
    case 'verbose':
    case 'silly':
      return 'DEBUG';
    default:
      return 'WARNING';
  }
}
