// Copyright 2023 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 Common from '../../../core/common/common.js';
import * as Extensions from '../../../models/extensions/extensions.js';
import * as PanelCommon from '../../common/common.js';

let instance: ExtensionManager|null = null;

export interface Extension {
  getName(): string;
  getMediaType(): string|undefined;
  stringify(recording: Object): Promise<string>;
  stringifyStep(step: Object): Promise<string>;
  getCapabilities(): Array<'replay'|'export'>;
  replay(recording: Object): void;
}

export class ExtensionManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
  static instance(): ExtensionManager {
    if (!instance) {
      instance = new ExtensionManager();
    }
    return instance;
  }

  #views = new Map<string, PanelCommon.ExtensionIframe.ExtensionIframe>();

  constructor() {
    super();
    this.attach();
  }

  attach(): void {
    const pluginManager = Extensions.RecorderPluginManager.RecorderPluginManager.instance();
    pluginManager.addEventListener(Extensions.RecorderPluginManager.Events.PLUGIN_ADDED, this.#handlePlugin);
    pluginManager.addEventListener(Extensions.RecorderPluginManager.Events.PLUGIN_REMOVED, this.#handlePlugin);
    pluginManager.addEventListener(Extensions.RecorderPluginManager.Events.VIEW_REGISTERED, this.#handleView);
    for (const descriptor of pluginManager.views()) {
      this.#handleView({data: descriptor});
    }
  }

  detach(): void {
    const pluginManager = Extensions.RecorderPluginManager.RecorderPluginManager.instance();
    pluginManager.removeEventListener(Extensions.RecorderPluginManager.Events.PLUGIN_ADDED, this.#handlePlugin);
    pluginManager.removeEventListener(Extensions.RecorderPluginManager.Events.PLUGIN_REMOVED, this.#handlePlugin);
    pluginManager.removeEventListener(Extensions.RecorderPluginManager.Events.VIEW_REGISTERED, this.#handleView);
    this.#views.clear();
  }

  extensions(): Extension[] {
    return Extensions.RecorderPluginManager.RecorderPluginManager.instance().plugins();
  }

  getView(descriptorId: string): PanelCommon.ExtensionIframe.ExtensionIframe {
    const view = this.#views.get(descriptorId);
    if (!view) {
      throw new Error('View not found');
    }
    return view;
  }

  #handlePlugin = (): void => {
    this.dispatchEventToListeners(Events.EXTENSIONS_UPDATED, this.extensions());
  };

  #handleView = (event: {data: Extensions.RecorderPluginManager.ViewDescriptor}): void => {
    const descriptor = event.data;
    if (!this.#views.has(descriptor.id)) {
      this.#views.set(descriptor.id, new PanelCommon.ExtensionIframe.ExtensionIframe(descriptor));
    }
  };
}

export const enum Events {
  EXTENSIONS_UPDATED = 'extensionsUpdated',
}

export interface EventTypes {
  [Events.EXTENSIONS_UPDATED]: Extension[];
}
