import { Overlay } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import { Injectable, Injector } from '@angular/core';
import { Toast, ToastRef } from './defs';
import { ToastComponent } from './toast.component';
import { TOAST_CONFIG_TOKEN } from './tokens';

@Injectable({
  providedIn: 'root',
})
export class ToastService {
  private _lastToastRef: ToastRef;

  get lastToastRef() {
    return this._lastToastRef;
  }

  set lastToastRef(toastRef: ToastRef) {
    this._lastToastRef = toastRef;
  }

  constructor(private readonly _overlay: Overlay, private readonly _injector: Injector) {}

  create(data: Toast) {
    const positionStrategy = this.getPositionStrategy(data);
    const overlayRef = this._overlay.create({ positionStrategy });

    this.lastToastRef = new ToastRef(overlayRef);
    const injector = this.getInjector(data, this.lastToastRef);

    const toastPortal = new ComponentPortal(ToastComponent, null, injector);
    overlayRef.attach(toastPortal);

    return this.lastToastRef;
  }

  getInjector(data: Toast, ref: ToastRef) {
    return Injector.create({
      parent: this._injector,
      providers: [
        { provide: TOAST_CONFIG_TOKEN, useValue: data },
        { provide: ToastRef, useValue: ref },
      ],
    });
  }

  getPositionStrategy(data: Toast) {
    switch (data?.position) {
      case 'top':
        return this._overlay.position().global().top(this.getNextPosition()).centerHorizontally();
      case 'top-left':
        return this._overlay.position().global().top(this.getNextPosition()).left('1rem');
      case 'top-right':
        return this._overlay.position().global().top(this.getNextPosition()).right('1rem');
      case 'bottom':
        return this._overlay.position().global().bottom(this.getNextPosition(true)).centerHorizontally();
      case 'bottom-left':
        return this._overlay.position().global().bottom(this.getNextPosition(true)).left('1rem');
      case 'bottom-right':
        return this._overlay.position().global().bottom(this.getNextPosition(true)).right('1rem');
      default:
        return this._overlay.position().global().top(this.getNextPosition()).centerHorizontally();
    }
  }

  getNextPosition(fromBottom?: boolean) {
    const lastToastIsVisible = this.lastToastRef && this.lastToastRef.isVisible();

    let position = fromBottom ? 6 : 9;

    if (lastToastIsVisible && fromBottom) {
      position = (window.innerHeight - this.lastToastRef.getPosition().bottom + this.lastToastRef.getPosition().height + 16) / 16;
    } else if (lastToastIsVisible) {
      position = (this.lastToastRef.getPosition().bottom + 16) / 16;
    }

    return position + 'rem';
  }
}
