import { Config } from '../../config';
import { AuthService } from '../../security/auth-service';
import * as signalR from "@microsoft/signalr";
import { Container, Disposable } from "aurelia-framework";
import { UIInternal } from '../../utils/ui-internal';

export abstract class AbstractHubClient {

  protected abstract get endpoint(): string;

  private signalRConnection: signalR.HubConnection;
  private isInitialized: boolean = false;
  private onReadyListeners: Function[];

  constructor() {
    this.onReadyListeners = [];
    UIInternal.subscribeOnce(UIInternal.EVT_APP_READY, () => this.initialize());
  }

  private async initialize() {
    if (this.isInitialized === false) {
      let endpoint = this.endpoint;
      if (false == endpoint.startsWith('/')) {
        endpoint = '/' + endpoint;
      }

      let config = Container.instance.get(Config);
      let authService = Container.instance.get(AuthService);

      this.signalRConnection = new signalR.HubConnectionBuilder()
        .withUrl(config.globalConfig.remoteServiceName + endpoint, { accessTokenFactory: async () => await authService.accessToken() })
        .withAutomaticReconnect()
        .build();

      try {
        await this.signalRConnection.start();
        this.isInitialized = true;
        this.onReadyListeners.forEach(x => x());
      } catch (err) {
        console.error(err);
      }
    }
  }

  public async whenReady() {
    if (this.isInitialized) {
      return;
    }
    let resolver: () => void;
    let promise = new Promise<void>(resolve => resolver = resolve);
    this.onReadyListeners.push(resolver);
    return promise;
  }

  protected subscribe(eventName: string, listener: (...args: any[]) => void): Disposable {
    this.signalRConnection.on(eventName, listener);
    return { dispose: () => this.signalRConnection.off(eventName, listener) };
  }

  protected async send(methodName: string, ...args: any[]) {
    await this.signalRConnection.send(methodName, ...args);
  }
}