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

import '../../ui/components/icon_button/icon_button.js';

import * as Common from '../../core/common/common.js';
import * as Host from '../../core/host/host.js';
import * as i18n from '../../core/i18n/i18n.js';
import * as SDK from '../../core/sdk/sdk.js';
import * as IssuesManager from '../../models/issues_manager/issues_manager.js';
import type * as IconButton from '../../ui/components/icon_button/icon_button.js';
import * as IssueCounter from '../../ui/components/issue_counter/issue_counter.js';
import * as UI from '../../ui/legacy/legacy.js';
import * as Lit from '../../ui/lit/lit.js';
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';

const {html, nothing, render} = Lit;

const UIStrings = {
  /**
   * @description The console error count in the Warning Error Counter shown in the main toolbar (top-left in DevTools). The error count refers to the number of errors currently present in the JavaScript console.
   */
  sErrors: '{n, plural, =1 {# error} other {# errors}}',
  /**
   * @description The console warning count in the Warning Error Counter shown in the main toolbar (top-left in DevTools). The warning count refers to the number of warnings currently present in the JavaScript console.
   */
  sWarnings: '{n, plural, =1 {# warning} other {# warnings}}',
  /**
   * @description Tooltip shown for a main toolbar button that opens the Console panel
   * @example {2 errors, 1 warning} PH1
   */
  openConsoleToViewS: 'Open Console to view {PH1}',
  /**
   * @description Title for the issues count in the Issues Error Counter shown in the main toolbar (top-left in DevTools). The issues count refers to the number of issues in the issues tab.
   */
  openIssuesToView: '{n, plural, =1 {Open Issues to view # issue:} other {Open Issues to view # issues:}}',
} as const;
const str_ = i18n.i18n.registerUIStrings('panels/console_counters/WarningErrorCounter.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);

interface ViewInput {
  compact?: boolean;
  errors: number;
  warnings: number;
  issues: number;
  showIssuesHandler: () => void;
}

type View = (input: ViewInput, output: object, target: HTMLElement) => void;

const DEFAULT_VIEW: View = (input, _output, target) => {
  const issuesManager = IssuesManager.IssuesManager.IssuesManager.instance();

  const countToText = (c: number): string|undefined => c === 0 ? undefined : `${c}`;
  const {errors, warnings, issues, compact} = input;

  /* Update consoleCounter items. */
  const errorCountTitle = i18nString(UIStrings.sErrors, {n: errors});
  const warningCountTitle = i18nString(UIStrings.sWarnings, {n: warnings});
  let consoleSummary = '';
  if (errors && warnings) {
    consoleSummary = `${errorCountTitle}, ${warningCountTitle}`;
  } else if (errors) {
    consoleSummary = errorCountTitle;
  } else if (warnings) {
    consoleSummary = warningCountTitle;
  }
  const consoleTitle = i18nString(UIStrings.openConsoleToViewS, {PH1: consoleSummary});
  const iconData: IconButton.IconButton.IconButtonData = {
    clickHandler: Common.Console.Console.instance().show.bind(Common.Console.Console.instance()),
    accessibleName: consoleTitle,
    compact,
    groups: [
      {iconName: 'cross-circle-filled', text: countToText(errors)},
      {iconName: 'warning-filled', text: countToText(warnings)},
    ],
  };

  /* Update issuesCounter items. */
  const issueEnumeration = IssueCounter.IssueCounter.getIssueCountsEnumeration(issuesManager);
  const issuesTitleLead = i18nString(UIStrings.openIssuesToView, {n: issues});
  const issuesTitle = `${issuesTitleLead} ${issueEnumeration}`;
  const issueCounterData: IssueCounter.IssueCounter.IssueCounterData = {
    clickHandler: input.showIssuesHandler,
    issuesManager,
    compact,
    accessibleName: issuesTitle,
    displayMode: IssueCounter.IssueCounter.DisplayMode.ONLY_MOST_IMPORTANT,
  };

  render(
      html`<div class="status-buttons"
         >${
          errors + warnings ? html`<icon-button
            .data=${iconData}
            title=${consoleTitle}
            class=${'small'}
            jslog=${VisualLogging.counter('console').track({
            click: true
          })}
         ></icon-button>` :
                              nothing}${
          issues ? html`<devtools-issue-counter
            class=${'main-toolbar'}
            title=${issuesTitle}
            .data=${issueCounterData}
            jslog=${VisualLogging.counter('issue').track({
            click: true
          })}
         ></devtools-issue-counter>` :
                   nothing}</div>`,
      target);
};

export class WarningErrorCounterWidget extends UI.Widget.Widget {
  private readonly throttler: Common.Throttler.Throttler;
  updatingForTest?: boolean;
  private compact?: boolean;
  static instanceForTest: WarningErrorCounterWidget|null = null;

  constructor(
      element: HTMLElement, private readonly setVisibility: (visible: boolean) => void,
      private readonly view: View = DEFAULT_VIEW) {
    super(element);
    this.throttler = new Common.Throttler.Throttler(100);

    SDK.TargetManager.TargetManager.instance().addModelListener(
        SDK.ConsoleModel.ConsoleModel, SDK.ConsoleModel.Events.ConsoleCleared, this.update, this);
    SDK.TargetManager.TargetManager.instance().addModelListener(
        SDK.ConsoleModel.ConsoleModel, SDK.ConsoleModel.Events.MessageAdded, this.update, this);
    SDK.TargetManager.TargetManager.instance().addModelListener(
        SDK.ConsoleModel.ConsoleModel, SDK.ConsoleModel.Events.MessageUpdated, this.update, this);

    const issuesManager = IssuesManager.IssuesManager.IssuesManager.instance();
    issuesManager.addEventListener(IssuesManager.IssuesManager.Events.ISSUES_COUNT_UPDATED, this.update, this);

    this.update();
    WarningErrorCounterWidget.instanceForTest = this;
  }

  onSetCompactLayout(event: Common.EventTarget.EventTargetEvent<boolean>): void {
    this.setCompactLayout(event.data);
  }

  setCompactLayout(enable: boolean): void {
    this.compact = enable;
    void this.performUpdate();
  }

  private updatedForTest(): void {
    // Sniffed in tests.
  }

  private update(): void {
    this.updatingForTest = true;
    void this.throttler.schedule(this.performUpdate.bind(this));
  }

  get titlesForTesting(): string|null {
    const button = this.contentElement.querySelector('icon-button')?.shadowRoot?.querySelector('button');
    return button ? button.getAttribute('aria-label') : null;
  }

  private showIssues(): void {
    Host.userMetrics.issuesPanelOpenedFrom(Host.UserMetrics.IssueOpener.STATUS_BAR_ISSUES_COUNTER);
    void UI.ViewManager.ViewManager.instance().showView('issues-pane');
  }

  override async performUpdate(): Promise<void> {
    const errors = SDK.ConsoleModel.ConsoleModel.allErrors();
    const warnings = SDK.ConsoleModel.ConsoleModel.allWarnings();
    const issuesManager = IssuesManager.IssuesManager.IssuesManager.instance();
    const issues = issuesManager.numberOfIssues();

    this.view(
        {compact: this.compact, errors, warnings, issues, showIssuesHandler: this.showIssues.bind(this)}, {},
        this.contentElement);

    this.setVisibility(Boolean(errors || warnings || issues));
    UI.InspectorView.InspectorView.instance().toolbarItemResized();

    this.updatingForTest = false;
    this.updatedForTest();
    return;
  }
}

let warningErrorCounterInstance: WarningErrorCounter;
export class WarningErrorCounter implements UI.Toolbar.Provider {
  private readonly toolbarItem: UI.Toolbar.ToolbarItemWithCompactLayout;

  private constructor() {
    const widgetElement =
        document.createElement('devtools-widget') as UI.Widget.WidgetElement<WarningErrorCounterWidget>;
    const toolbarItem = new UI.Toolbar.ToolbarItemWithCompactLayout(widgetElement);
    toolbarItem.setVisible(false);

    const widget = new WarningErrorCounterWidget(widgetElement, toolbarItem.setVisible.bind(toolbarItem));
    toolbarItem.addEventListener(
        UI.Toolbar.ToolbarItemWithCompactLayoutEvents.COMPACT_LAYOUT_UPDATED, widget.onSetCompactLayout, widget);

    this.toolbarItem = toolbarItem;
  }

  item(): UI.Toolbar.ToolbarItem|null {
    return this.toolbarItem;
  }

  static instance(opts: {forceNew: boolean|null} = {forceNew: null}): WarningErrorCounter {
    const {forceNew} = opts;
    if (!warningErrorCounterInstance || forceNew) {
      warningErrorCounterInstance = new WarningErrorCounter();
    }

    return warningErrorCounterInstance;
  }
}
