import {
  Overlay,
  OverlayConfig,
  OverlayRef,
  ConnectedPosition,
} from '@angular/cdk/overlay';
import {
  ComponentPortal,
  PortalInjector,
} from '@angular/cdk/portal';
import {
  Injectable,
  Injector,
} from '@angular/core';

// Токены
import {
  POPUP_CONFIG,
  POPUP_DATA,
} from '../tokens';

// Интерфейсы
import { IPopupConfig } from '../interfaces';

// Сервис всплывающих окон
@Injectable({ providedIn: 'root' })
export class HitOverlayService {

  constructor(
    private readonly injector: Injector,
    private readonly overlay: Overlay,
  ) {}

  // Открыть контекстное всплывающее окно
  public openContext(config: IPopupConfig, data: any = {}): OverlayRef {

    const defaultPositions = [
      {
        overlayX: 'end', overlayY: 'top',
        originX: 'end', originY: 'bottom',
      },
      {
        overlayX: 'end', overlayY: 'bottom',
        originX: 'end', originY: 'top',
      },
    ] as ConnectedPosition[];

    const positions = config.positions
      ? config.positions
      : defaultPositions;

    // Стратегия подключения
    const positionStrategy = this.overlay.position()
      .flexibleConnectedTo(config.elementRef)
      .withPositions(positions);

    // Конфигурация
    const overlayConfig = new OverlayConfig({
      positionStrategy,
    });

    return this.createOverlay(overlayConfig, config, data);
  }

  // Открыть всплывающее окно
  public openPopup(config: IPopupConfig, data: any = {}): OverlayRef {

    // Стратегия подключения
    const positionStrategy = this.overlay.position()
      .global()
      .centerHorizontally()
      .centerVertically();

    // Конфигурация
    const overlayConfig = new OverlayConfig({
      positionStrategy,
      hasBackdrop: true,
      backdropClass: 'popup-backdrop',
      scrollStrategy: this.overlay.scrollStrategies.block(),
    });

    const overlayRef = this.createOverlay(overlayConfig, config, data);

    overlayRef
      .backdropClick()
      .subscribe(() => { overlayRef.dispose(); });

    return overlayRef;
  }

  // Создать окно
  private createOverlay(
    overlayConfig: OverlayConfig,
    config: IPopupConfig,
    data: any,
  ): OverlayRef {

    // Всплывающее окно
    const overlayRef = this.overlay.create(overlayConfig);

    // Инжектор
    const injector = this.createInjector(overlayRef, config, data);

    // Создание экземпляра компонента
    const portal = new ComponentPortal<any>(
      config.overlayComponent,
      null,
      injector,
    );

    // Подключение созданного компонента к слою всплывающего окна
    overlayRef.attach(portal);

    return overlayRef;
  }

  // Создать инжектор
  private createInjector(
    overlayRef: OverlayRef,
    config: IPopupConfig,
    data: any = null,
  ): PortalInjector {

    const injectionTokens = new WeakMap();

    injectionTokens.set(OverlayRef, overlayRef);
    injectionTokens.set(POPUP_CONFIG, config);
    injectionTokens.set(POPUP_DATA, data);

    return new PortalInjector(this.injector, injectionTokens);
  }
}
