import { IApplication } from '@api-client/core/build/browser.js';
import { AddConfigEnvironmentInit, ConfigurationBindings, EnvironmentConfiguration, LocalConfiguration, SessionConfiguration, TelemetryConfiguration } from '../base/ConfigurationBindings.js';
import { IoCommand, IoEvent, IoNotification } from '../base/PlatformBindings.js';
import { IConfigEnvironment, ITelemetryConfig, IEnvConfig } from '../../lib/config/Config.js';

/**
 * Application configuration bindings that handles storing application configuration
 * on the web platform.
 */
export class WebConfigurationBindings extends ConfigurationBindings {
  worker?: SharedWorker;

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

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

  async createWorker(): Promise<void> {
    return new Promise((resolve, reject) => {
      const worker = new SharedWorker(this.workerUrl, {
        type: 'module',
        name: 'AppConfig',
      });
      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 () {
        worker.port.removeEventListener('message', init);
        worker.port.addEventListener('message', binder);
        resolve();
      });
      setTimeout(() => {
        worker.port.postMessage('init');
      }, 1);
    });
  }

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

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

  local: LocalConfiguration = {
    get: async (key: string): Promise<unknown | undefined> => this.invoke('local.get', key),

    set: async (key: string, value: unknown): Promise<void> => this.invoke('local.set', key, value),

    delete: async (key: string): Promise<void> => this.invoke('local.delete', key),
  }

  session: SessionConfiguration = {
    get: async (key: string): Promise<unknown> => this.invoke('session.get', key),

    set: async (key: string, value: unknown): Promise<void> => this.invoke('session.set', key, value),

    delete: async (key: string): Promise<void> => this.invoke('session.delete', key),
  }

  telemetry: TelemetryConfiguration = {
    read: async (): Promise<ITelemetryConfig> => this.invoke('telemetry.read'),

    set: async (config: ITelemetryConfig): Promise<void> => this.invoke('telemetry.set', config),
  }

  environment: EnvironmentConfiguration = {
    add: async (env: IConfigEnvironment, init: AddConfigEnvironmentInit = {}): Promise<void> => this.invoke('environment.add', env, init),

    update: async (env: IConfigEnvironment): Promise<void> => this.invoke('environment.update', env),

    read: async (id?: string): Promise<IConfigEnvironment> => this.invoke('environment.read', id),

    remove: async (id: string): Promise<void> => this.invoke('environment.remove', id),

    setDefault: async (id: string): Promise<void> => this.invoke('environment.setDefault', id),

    list: async (): Promise<IEnvConfig> => this.invoke('environment.list')
  }
}
