import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import { ComponentRef, Inject, Injectable } from '@angular/core';
import { Observable, Subscription } from 'rxjs';
import { take } from 'rxjs/operators';
import { NotificationOptions } from '../common/interfaces';
import { WINDOW } from '../common/tokens';
import { NotificationComponent } from '../components/notification/notification.component';

@Injectable({
  providedIn: 'root'
})
export class NotificationController {
  private overlayConfig: OverlayConfig;
  private overlayRef: OverlayRef;
  private componentRef: ComponentRef<NotificationComponent>;
  /** 通知关闭计时器 */
  private dismissTimeout: number;
  /** 一个订阅器 */
  private subscription: Subscription;

  options: NotificationOptions;

  constructor(
    private overlay: Overlay,
    @Inject(WINDOW) private window: Window,
  ) {
    this.overlayConfig = new OverlayConfig({
      // 全局显示,水平居中,位于顶部
      positionStrategy: this.overlay.position().global().centerHorizontally().top()
    });
  }

  /**
   * 创建一个通知
   * @param opts
   */
  create(options: NotificationOptions): NotificationController {
    this.options = options;

    this.overlayRef ??= this.overlay.create(this.overlayConfig);

    return this;
  }

  /**
   * 弹出通知
   * 如果通知已经存在,则更新内容并重新计时
   */
  present(): NotificationController {
    this.dismissTimeout && this.clearDismissTimeout();
    this.subscription && !this.subscription.closed && this.subscription.unsubscribe();

    this.componentRef ??= this.overlayRef.attach(new ComponentPortal(NotificationComponent));
    this.componentRef.instance.overlayRef = this.overlayRef;

    const { title, description, icon, duration, url, handler } = this.options;

    this.componentRef.instance.title = title;
    this.componentRef.instance.description = description;
    this.componentRef.instance.icon = icon;
    this.componentRef.instance.url = url;
    this.componentRef.instance.handler = handler;

    // 监听通知关闭事件
    this.subscription = this.componentRef.instance.onDismiss().pipe(take(1)).subscribe(() => {
      this.clearRef();
      this.clearDismissTimeout();
    });

    // 开始计时
    this.dismissTimeout = this.window.setTimeout(() => this.dismiss(), duration || 5000);

    return this;
  }

  /**
   * 关闭通知
   */
  dismiss(): Observable<void> {
    this.componentRef.instance.dismiss().pipe(take(1)).subscribe(() => {
      this.clearRef();
      this.clearDismissTimeout();
    });

    return this.componentRef.instance.dismiss();
  }

  private clearDismissTimeout(): void {
    this.window.clearTimeout(this.dismissTimeout);
    this.dismissTimeout = null;
  }

  private clearRef(): void {
    this.componentRef = null;
    this.overlayRef = null;
  }
}