// Copyright 2019 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/legacy/components/data_grid/data_grid.js';

import * as i18n from '../../core/i18n/i18n.js';
import * as SourceFrame from '../../ui/legacy/components/source_frame/source_frame.js';
import * as UI from '../../ui/legacy/legacy.js';
import {Directives, html, render} from '../../ui/lit/lit.js';
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';

import eventDisplayTableStyles from './eventDisplayTable.css.js';
import type {PlayerEvent} from './MediaModel.js';

const {widget} = UI.Widget;
const {repeat} = Directives;
const UIStrings = {
  /**
   * @description Text for timestamps of items.
   */
  timestamp: 'Timestamp',
  /**
   * @description The column header for event names.
   */
  eventName: 'Event name',
  /**
   * @description Text for the value of something.
   */
  value: 'Value',
  /**
   * @description The accessible name of a table that displays information about events that occurred
   * while a video/media player was present on the page.
   */
  eventDisplay: 'Event display',
} as const;
const str_ = i18n.i18n.registerUIStrings('panels/media/EventDisplayTable.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
export interface EventDisplayRow {
  displayTimestamp: string;
  event: string;
  value: Record<string, unknown>;
}

export interface PlayerEventsViewInput {
  parsedEvents: EventDisplayRow[];
}

export type View = (input: PlayerEventsViewInput, output: void, target: HTMLElement) => void;

export const DEFAULT_VIEW: View = (input, _output, target) => {
  // clang-format off
  render(html`
      <style>${eventDisplayTableStyles}</style>
      <devtools-data-grid row-height='auto' autoscroll striped name=${i18nString(UIStrings.eventDisplay)}
        class="event-display-table-contents-table-container no-border-top-datagrid"
        .template=${html`
          <style>${eventDisplayTableStyles}</style>
          <table>
            <thead>
              <tr>
                <th id="display-timestamp" weight="1">${i18nString(UIStrings.timestamp)}</th>
                <th id="event" weight="2">${i18nString(UIStrings.eventName)}</th>
                <th id="value" weight="7">${i18nString(UIStrings.value)}</th>
              </tr>
            </thead>
            <tbody>
              ${repeat(input.parsedEvents, event => event, event => html`
                <tr>
                  <td class="event-display-table-basic-text-table-entry">${event.displayTimestamp}</td>
                  <td class="event-display-table-basic-text-table-entry">${event.event}</td>
                  <td class="event-display-table-contents-json-wrapper">
                    ${widget(el => new SourceFrame.JSONView.JSONView(new SourceFrame.JSONView.ParsedJSON(event.value, '', ''), true, el))}
                  </td>
                </tr>
              `)}
            </tbody>
          </table>
        `}>
      </devtools-data-grid>
    `,
         // clang-format on
         target);
};

export class PlayerEventsView extends UI.Widget.VBox {
  private firstEventTime: number;
  #view: View;
  #parsedEvents: EventDisplayRow[] = [];

  constructor(view: View = DEFAULT_VIEW) {
    super({jslog: `${VisualLogging.pane('events')}`});
    this.#view = view;
    this.firstEventTime = 0;
    this.requestUpdate();
  }

  override wasShown(): void {
    super.wasShown();
    this.requestUpdate();
  }

  override performUpdate(): void {
    const viewInput = {
      parsedEvents: this.#parsedEvents,
    };
    this.#view(viewInput, undefined, this.contentElement);
  }

  onEvent(event: PlayerEvent): void {
    if (this.firstEventTime === 0 && typeof event.timestamp === 'number') {
      this.firstEventTime = event.timestamp;
    }

    event = this.subtractFirstEventTime(event);
    const stringified = event.value;
    try {
      const json = JSON.parse(stringified);
      event.event = json.event;
      delete json['event'];
      this.#parsedEvents.push({
        displayTimestamp: event.displayTimestamp,
        event: event.event,
        value: json,
      });
      this.requestUpdate();
    } catch {
      // If this is a legacy message event, ignore it for now until they
      // are handled.
    }
  }

  private subtractFirstEventTime(event: PlayerEvent): PlayerEvent {
    if (typeof event.timestamp === 'number') {
      event.displayTimestamp = (event.timestamp - this.firstEventTime).toFixed(3);
    }
    return event;
  }
}
