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

import * as Trace from '../../../models/trace/trace.js';
import {raf, renderElementIntoDOM} from '../../../testing/DOMHelpers.js';
import {describeWithEnvironment} from '../../../testing/EnvironmentHelpers.js';
import {TraceLoader} from '../../../testing/TraceLoader.js';
import * as RenderCoordinator from '../../../ui/components/render_coordinator/render_coordinator.js';

import * as Components from './components.js';

describeWithEnvironment('Sidebar', () => {
  async function renderSidebar(
      parsedTrace: Trace.Handlers.Types.ParsedTrace,
      metadata: Trace.Types.File.MetaData|null,
      insights: Trace.Insights.Types.TraceInsightSets|null,
      ): Promise<Components.Sidebar.SidebarWidget> {
    const container = document.createElement('div');
    renderElementIntoDOM(container);
    const sidebar = new Components.Sidebar.SidebarWidget();
    sidebar.markAsRoot();
    sidebar.setParsedTrace(parsedTrace, metadata);
    sidebar.setInsights(insights);
    sidebar.show(container);
    await raf();
    return sidebar;
  }

  it('renders with two tabs for insights & annotations', async function() {
    const {parsedTrace, metadata, insights} = await TraceLoader.traceEngine(this, 'web-dev-with-commit.json.gz');

    const sidebar = await renderSidebar(parsedTrace, metadata, insights);
    const tabbedPane = sidebar.element.querySelector('.tabbed-pane')?.shadowRoot;
    assert.isOk(tabbedPane);
    const tabs = Array.from(tabbedPane.querySelectorAll('[role="tab"]'));
    assert.lengthOf(tabs, 2);
    const labels = tabs.map(elem => elem.getAttribute('aria-label'));
    assert.deepEqual(labels, ['Insights', 'Annotations']);
  });

  it('selects the insights tab by default', async function() {
    const {parsedTrace, metadata, insights} = await TraceLoader.traceEngine(this, 'web-dev-with-commit.json.gz');

    const sidebar = await renderSidebar(parsedTrace, metadata, insights);
    const tabbedPane = sidebar.element.querySelector('.tabbed-pane')?.shadowRoot;
    assert.isOk(tabbedPane);

    const tabs = Array.from(tabbedPane.querySelectorAll('[role="tab"]'));
    const selectedTabLabels =
        tabs.filter(tab => tab.classList.contains('selected')).map(elem => elem.getAttribute('aria-label'));
    assert.deepEqual(selectedTabLabels, ['Insights']);
  });

  it('disables the insights tab if there are no insights', async function() {
    const {parsedTrace, metadata} = await TraceLoader.traceEngine(this, 'web-dev-with-commit.json.gz');
    const sidebar = await renderSidebar(parsedTrace, metadata, null);
    const tabbedPane = sidebar.element.querySelector('.tabbed-pane')?.shadowRoot;
    assert.isOk(tabbedPane);
    const tabs = Array.from(tabbedPane.querySelectorAll('[role="tab"]'));

    const disabledTabLabels =
        tabs.filter(tab => tab.classList.contains('disabled')).map(elem => elem.getAttribute('aria-label'));
    assert.deepEqual(disabledTabLabels, ['Insights']);

    const selectedTabLabels =
        tabs.filter(tab => tab.classList.contains('selected')).map(elem => elem.getAttribute('aria-label'));
    assert.deepEqual(selectedTabLabels, ['Annotations']);
  });

  it('shows the count for the active annotations', async function() {
    const {parsedTrace, metadata} = await TraceLoader.traceEngine(this, 'web-dev-with-commit.json.gz');
    const events = parsedTrace.Renderer.allTraceEntries;
    const annotation1: Trace.Types.File.Annotation = {
      type: 'ENTRY_LABEL',
      entry: events[0],
      label: 'Entry Label 1',
    };

    const annotation2: Trace.Types.File.Annotation = {
      type: 'ENTRY_LABEL',
      entry: events[1],
      label: 'Entry Label 2',
    };

    const sidebar = await renderSidebar(parsedTrace, metadata, null);
    sidebar.setAnnotations([annotation1, annotation2], new Map());
    const tabbedPane = sidebar.element.querySelector('.tabbed-pane')?.shadowRoot;
    assert.isOk(tabbedPane);
    const annotationsTab = tabbedPane.querySelector('#tab-annotations');
    assert.isOk(annotationsTab);
    const countBadge = annotationsTab.querySelector<HTMLElement>('.badge');
    assert.strictEqual(countBadge?.innerText, '2');
  });

  it('de-duplicates annotations that are pending to not show an incorrect count', async function() {
    const {parsedTrace, metadata} = await TraceLoader.traceEngine(this, 'web-dev-with-commit.json.gz');
    const events = parsedTrace.Renderer.allTraceEntries;

    // Create Empty Entry Label Annotation (considered not started)
    const entryLabelAnnotation: Trace.Types.File.Annotation = {
      type: 'ENTRY_LABEL',
      entry: events[0],
      label: '',
    };

    // Create Entries link that only has 'to' entry (considered not started)
    const entriesLink: Trace.Types.File.Annotation = {
      type: 'ENTRIES_LINK',
      entryFrom: events[0],
      state: Trace.Types.File.EntriesLinkState.CREATION_NOT_STARTED,
    };

    const sidebar = await renderSidebar(parsedTrace, metadata, null);
    sidebar.setAnnotations([entryLabelAnnotation, entriesLink], new Map());
    const tabbedPane = sidebar.element.querySelector('.tabbed-pane')?.shadowRoot;
    assert.isOk(tabbedPane);
    const annotationsTab = tabbedPane.querySelector('#tab-annotations');
    assert.isOk(annotationsTab);
    const countBadge = annotationsTab.querySelector<HTMLElement>('.badge');
    assert.strictEqual(countBadge?.innerText, '1');
  });

  it('removes the annotations badge when the user deletes the final annotation', async function() {
    const {parsedTrace, metadata} = await TraceLoader.traceEngine(this, 'web-dev-with-commit.json.gz');
    const entryLabelAnnotation: Trace.Types.File.Annotation = {
      type: 'ENTRY_LABEL',
      entry: parsedTrace.Renderer.allTraceEntries[0],  // random event, doesn't matter
      label: 'hello world',
    };

    const sidebar = await renderSidebar(parsedTrace, metadata, null);
    sidebar.setAnnotations([entryLabelAnnotation], new Map());
    await RenderCoordinator.done();
    const tabbedPane = sidebar.element.querySelector('.tabbed-pane')?.shadowRoot;
    assert.isOk(tabbedPane);
    const annotationsTab = tabbedPane.querySelector('#tab-annotations');
    assert.isOk(annotationsTab);
    const countBadge = annotationsTab.querySelector<HTMLElement>('.badge');
    assert.strictEqual(countBadge?.innerText, '1');

    sidebar.setAnnotations([], new Map());  // delete the annotations
    const updatedCountBadge = annotationsTab.querySelector<HTMLElement>('.badge');
    assert.isNull(updatedCountBadge);  // No badge is shown when the count is 0.
  });
});
