import {ComponentPortal, ComponentType, DomPortalOutlet, TemplatePortal} from '@angular/cdk/portal';
import {ApplicationRef, ComponentFactoryResolver, ComponentRef, Injectable, TemplateRef} from '@angular/core';
import {ToastContainerComponent} from './toast-container.component';
import {ToastContentConfig, ToastServiceConfig} from './toast.model';

@Injectable()
export class ToastService {
  private portalHost: DomPortalOutlet | undefined;
  private containerPortalHost: DomPortalOutlet | undefined;
  private containerRef: ComponentRef<ToastContainerComponent> | undefined;

  private config: ToastServiceConfig = {
    containerId: 'nj-toast-container-id',
    isContainerFullWidth: false
  };

  constructor(private componentFactoryResolver: ComponentFactoryResolver, private appRef: ApplicationRef) {
  }

  /**
   * Opens a toast wrapped inside a ng-template or a Component
   */
  open(content: TemplateRef<any> | ComponentType<any>, config: ToastContentConfig) {
    if (!content) {
      return;
    }
    let toastPortal;
    if (!this.containerRef) {
      this.attachContainer();
    }
    const containerElement = this.containerRef?.location?.nativeElement?.children?.[0];
    this.containerPortalHost = new DomPortalOutlet(containerElement);
    if (content instanceof TemplateRef) {
      toastPortal = new TemplatePortal(content, config.viewContainerRef, {
        $implicit: config.data
      });
    } else {
      toastPortal = new ComponentPortal(content, config.viewContainerRef, null, this.componentFactoryResolver);
    }
    return this.containerPortalHost.attach(toastPortal);
  }

  /**
   * Set config
   */
  setConfig(config: ToastServiceConfig) {
    this.config = {...this.config, ...config};
    this.setContainerInputs(this.containerRef);
  }

  private setContainerInputs(containerComponentRef: ComponentRef<ToastContainerComponent> | undefined) {
    const containerComponent = containerComponentRef?.instance;
    if (containerComponent) {
      containerComponent.containerId = this.config?.containerId;
      containerComponent.isFullWidth = this.config?.isContainerFullWidth;
      containerComponent._markForCheck();
    }
  }

  private attachContainer() {
    this.portalHost = new DomPortalOutlet(document.body, undefined, this.appRef);
    const containerPortal = new ComponentPortal(ToastContainerComponent, null, null, this.componentFactoryResolver);
    const containerComponentRef = this.portalHost.attachComponentPortal(containerPortal);
    this.setContainerInputs(containerComponentRef);
    this.containerRef = containerComponentRef;
    return containerComponentRef;
  }
}
