/* eslint-disable @typescript-eslint/no-unused-vars */
import { 
  ContextChangeRecord, ContextDeleteRecord, 
  IApplication, 
  IQueryResponse, 
  IRequestUiMeta, IUrl, IBulkOperationResult} from "@api-client/core/build/browser.js";
import { HttpClientStoreBindings } from "../base/HttpClientStoreBindings.js";
import { IoCommand, IoEvent, IoNotification } from "../base/PlatformBindings.js";

/**
 * HTTP Client's specific data (URL history, UI state, etc).
 */
export class WebHttpClientStoreBindings extends HttpClientStoreBindings {
  worker?: SharedWorker;

  constructor(app: IApplication, protected workerUrl: string) {
    super(app);
  }

  override async initialize(): Promise<void> {
    await this.createWorker();
    await this.initializeStore();
    await super.initialize();
  }

  async initializeStore(): Promise<void> {
    await this.invoke('initializeStore');
  }

  async createWorker(): Promise<void> {
    return new Promise((resolve, reject) => {
      const worker = new SharedWorker(this.workerUrl, {
        type: 'module',
        name: 'HttpClientStore',
      });
      worker.port.start();
      worker.onerror = (): void => reject(new Error('Unable to initialize the IO process.'));
      this.worker = worker;
      const binder = this.ioHandler.bind(this);
      worker.port.addEventListener('message', function init() {
        // the ready message.
        worker.port.removeEventListener('message', init);
        worker.port.addEventListener('message', binder);
        resolve();
      });
      setTimeout(() => {
        worker.port.postMessage('init');
      }, 1);
    });
  }

  protected override sendIoCommand(cmd: IoCommand): void {
    const { worker } = this;
    if (!worker) {
      throw new Error(`IO not ready.`);
    }
    worker.port.postMessage(cmd);
  }

  protected ioHandler(e: MessageEvent): void {
    if (e.data === 'ready') {
      return;
    }
    const event = e.data as IoCommand | IoEvent | IoNotification;
    this.handleIoMessage(event);
  }

  wsHistory = {
    add: (url: string): Promise<IUrl> => this.invoke('wsHistory.add', url),
    query: (query: string): Promise<IQueryResponse<IUrl>> => this.invoke('wsHistory.query', query),
    delete: (url: string): Promise<ContextDeleteRecord> => this.invoke('wsHistory.delete', url),
    clear: (): Promise<void> => this.invoke('wsHistory.clear'),
  }

  urlHistory = {
    add: (url: string): Promise<IUrl> => this.invoke('urlHistory.add', url),
    query: (query: string): Promise<IQueryResponse<IUrl>> => this.invoke('urlHistory.query', query),
    delete: (url: string): Promise<ContextDeleteRecord> => this.invoke('urlHistory.delete', url),
    clear: (): Promise<void> => this.invoke('urlHistory.clear'),
  }

  projectUi = {
    clear: (key: string): Promise<ContextDeleteRecord> => this.invoke('projectUi.clear', key),
    set: (pid: string, key: string, meta: IRequestUiMeta): Promise<ContextChangeRecord<IRequestUiMeta>> => this.invoke('projectUi.set', pid, key, meta),
    get: (pid: string, key: string): Promise<IRequestUiMeta | undefined> => this.invoke('projectUi.get', pid, key),
    getBulk: (pid: string, keys: string[]): Promise<IBulkOperationResult<IRequestUiMeta>> => this.invoke('projectUi.getBulk', pid, keys),
    delete: (pid: string, key: string): Promise<ContextDeleteRecord> => this.invoke('projectUi.delete', pid, key),
  }
}
