// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import * as Root from '../root/root.js';

import {ObjectWrapper} from './Object.js';
import {reveal} from './Revealer.js';

export class Console extends ObjectWrapper<EventTypes> {
  readonly #messages: Message[] = [];

  static instance(opts?: {forceNew: boolean}): Console {
    if (!Root.DevToolsContext.globalInstance().has(Console) || opts?.forceNew) {
      Root.DevToolsContext.globalInstance().set(Console, new Console());
    }

    return Root.DevToolsContext.globalInstance().get(Console);
  }

  static removeInstance(): void {
    Root.DevToolsContext.globalInstance().delete(Console);
  }

  /**
   * Add a message to the Console panel.
   *
   * @param text the message text.
   * @param level the message level.
   * @param show whether to show the Console panel (if it's not already shown).
   * @param source the message source.
   */
  addMessage(text: string, level = MessageLevel.INFO, show = false, source?: FrontendMessageSource): void {
    const message = new Message(text, level, Date.now(), show, source);
    this.#messages.push(message);
    this.dispatchEventToListeners(Events.MESSAGE_ADDED, message);
  }

  log(text: string): void {
    this.addMessage(text, MessageLevel.INFO);
  }

  warn(text: string, source?: FrontendMessageSource): void {
    this.addMessage(text, MessageLevel.WARNING, undefined, source);
  }

  /**
   * Adds an error message to the Console panel.
   *
   * @param text the message text.
   * @param show whether to show the Console panel (if it's not already shown).
   */
  error(text: string, show = true): void {
    this.addMessage(text, MessageLevel.ERROR, show);
  }

  messages(): Message[] {
    return this.#messages;
  }

  show(): void {
    void this.showPromise();
  }

  showPromise(): Promise<void> {
    return reveal(this);
  }
}

export const enum Events {
  MESSAGE_ADDED = 'messageAdded',
}

export interface EventTypes {
  [Events.MESSAGE_ADDED]: Message;
}

export const enum MessageLevel {
  INFO = 'info',
  WARNING = 'warning',
  ERROR = 'error',
}

export enum FrontendMessageSource {
  CSS = 'css',
  // eslint-disable-next-line @typescript-eslint/naming-convention -- Used by web_tests.
  ConsoleAPI = 'console-api',
  ISSUE_PANEL = 'issue-panel',
  SELF_XSS = 'self-xss',
}

export class Message {
  text: string;
  level: MessageLevel;
  timestamp: number;
  show: boolean;
  source?: FrontendMessageSource;
  constructor(text: string, level: MessageLevel, timestamp: number, show: boolean, source?: FrontendMessageSource) {
    this.text = text;
    this.level = level;
    this.timestamp = (typeof timestamp === 'number') ? timestamp : Date.now();
    this.show = show;
    if (source) {
      this.source = source;
    }
  }
}
