import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { Emitter, Event } from '@theia/core/lib/common/event';
import { MessageService } from '@theia/core/lib/common/message-service';
import { deepClone } from '@theia/core/lib/common/objects';
import URI from '@theia/core/lib/common/uri';
import {
  inject,
  injectable,
  postConstruct,
} from '@theia/core/shared/inversify';
import { ConfigService, ConfigState } from '../../common/protocol';
import { NotificationCenter } from '../notification-center';

@injectable()
export class ConfigServiceClient implements FrontendApplicationContribution {
  @inject(ConfigService)
  private readonly delegate: ConfigService;
  @inject(NotificationCenter)
  private readonly notificationCenter: NotificationCenter;
  @inject(FrontendApplicationStateService)
  private readonly appStateService: FrontendApplicationStateService;
  @inject(MessageService)
  private readonly messageService: MessageService;

  private readonly didChangeSketchDirUriEmitter = new Emitter<
    URI | undefined
  >();
  private readonly didChangeDataDirUriEmitter = new Emitter<URI | undefined>();
  private readonly toDispose = new DisposableCollection(
    this.didChangeSketchDirUriEmitter,
    this.didChangeDataDirUriEmitter
  );

  private config: ConfigState | undefined;

  @postConstruct()
  protected init(): void {
    this.appStateService.reachedState('ready').then(async () => {
      const config = await this.delegate.getConfiguration();
      this.use(config);
    });
  }

  onStart(): void {
    this.notificationCenter.onConfigDidChange((config) => this.use(config));
  }

  onStop(): void {
    this.toDispose.dispose();
  }

  get onDidChangeSketchDirUri(): Event<URI | undefined> {
    return this.didChangeSketchDirUriEmitter.event;
  }

  get onDidChangeDataDirUri(): Event<URI | undefined> {
    return this.didChangeDataDirUriEmitter.event;
  }

  /**
   * CLI config related error messages if any.
   */
  tryGetMessages(): string[] | undefined {
    return this.config?.messages;
  }

  /**
   * `directories.user`
   */
  tryGetSketchDirUri(): URI | undefined {
    return this.config?.config?.sketchDirUri
      ? new URI(this.config?.config?.sketchDirUri)
      : undefined;
  }

  /**
   * `directories.data`
   */
  tryGetDataDirUri(): URI | undefined {
    return this.config?.config?.dataDirUri
      ? new URI(this.config?.config?.dataDirUri)
      : undefined;
  }

  private use(config: ConfigState): void {
    const oldConfig = deepClone(this.config);
    this.config = config;
    if (oldConfig?.config?.sketchDirUri !== this.config?.config?.sketchDirUri) {
      this.didChangeSketchDirUriEmitter.fire(this.tryGetSketchDirUri());
    }
    if (oldConfig?.config?.dataDirUri !== this.config?.config?.dataDirUri) {
      this.didChangeDataDirUriEmitter.fire(this.tryGetDataDirUri());
    }
    if (this.config.messages?.length) {
      const message = this.config.messages.join(' ');
      // toast the error later otherwise it might not show up in IDE2
      setTimeout(() => this.messageService.error(message), 1_000);
    }
  }
}