import {
  ElementRef,
  Injectable,
  inject
} from '@angular/core';
import {
  ConnectedPosition,
  Overlay,
  OverlayRef
} from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import {
  Subscription,
  filter,
  fromEvent
} from 'rxjs';
import { PluginActionMenuComponent } from './plugin-action-menu.component';
import { ViewportService } from '../../../../core/platform';

const GAP = 10;
const VIEWPORT_MARGIN = 8;
const POSITIONS: ConnectedPosition[] = [
  { originX: 'end', originY: 'top', overlayX: 'start', overlayY: 'top', offsetX: GAP },
  { originX: 'end', originY: 'bottom', overlayX: 'start', overlayY: 'bottom', offsetX: GAP },
  { originX: 'start', originY: 'top', overlayX: 'end', overlayY: 'top', offsetX: -GAP },
  { originX: 'start', originY: 'bottom', overlayX: 'end', overlayY: 'bottom', offsetX: -GAP }
];

@Injectable({ providedIn: 'root' })
export class PluginActionMenuService {
  private readonly overlay = inject(Overlay);
  private readonly viewport = inject(ViewportService);
  private currentOrigin: HTMLElement | null = null;
  private overlayRef: OverlayRef | null = null;
  private overlaySubscriptions: Subscription | null = null;
  private scrollBlocker: (() => void) | null = null;

  open(origin: ElementRef | HTMLElement): void {
    const rawEl = origin instanceof ElementRef ? origin.nativeElement : origin;

    if (this.overlayRef) {
      const sameOrigin = rawEl === this.currentOrigin;

      this.close();

      if (sameOrigin) {
        return;
      }
    }

    const elementRef = origin instanceof ElementRef ? origin : new ElementRef(origin);
    const isMobile = this.viewport.isMobile();

    this.currentOrigin = rawEl;

    if (isMobile) {
      const positionStrategy = this.overlay
        .position()
        .global()
        .left('0')
        .right('0')
        .bottom('0');

      this.overlayRef = this.overlay.create({
        positionStrategy,
        scrollStrategy: this.overlay.scrollStrategies.block(),
        hasBackdrop: true,
        backdropClass: 'cdk-overlay-dark-backdrop',
        panelClass: 'metoyou-bottom-sheet-panel'
      });
    } else {
      const positionStrategy = this.overlay
        .position()
        .flexibleConnectedTo(elementRef)
        .withPositions(POSITIONS)
        .withViewportMargin(VIEWPORT_MARGIN)
        .withPush(true);

      this.overlayRef = this.overlay.create({
        positionStrategy,
        scrollStrategy: this.overlay.scrollStrategies.noop()
      });
    }

    this.syncThemeVars();

    const componentRef = this.overlayRef.attach(new ComponentPortal(PluginActionMenuComponent));
    const subscriptions = new Subscription();

    subscriptions.add(componentRef.instance.closed.subscribe(() => this.close()));

    if (isMobile) {
      subscriptions.add(this.overlayRef.backdropClick().subscribe(() => this.close()));
      this.overlaySubscriptions = subscriptions;

      return;
    }

    subscriptions.add(fromEvent<PointerEvent>(document, 'pointerdown')
      .pipe(
        filter((event) => {
          const target = event.target as Node;

          if (this.overlayRef?.overlayElement.contains(target)) {
            return false;
          }

          if (this.currentOrigin?.contains(target)) {
            return false;
          }

          return true;
        })
      )
      .subscribe(() => this.close()));

    this.overlaySubscriptions = subscriptions;
    this.blockScroll();
  }

  close(): void {
    this.scrollBlocker?.();
    this.scrollBlocker = null;
    this.overlaySubscriptions?.unsubscribe();
    this.overlaySubscriptions = null;

    if (this.overlayRef) {
      this.overlayRef.dispose();
      this.overlayRef = null;
      this.currentOrigin = null;
    }
  }

  private blockScroll(): void {
    const handler = (event: Event): void => {
      if (this.overlayRef?.overlayElement.contains(event.target as Node)) {
        return;
      }

      event.preventDefault();
    };
    const opts: AddEventListenerOptions = { passive: false, capture: true };

    document.addEventListener('wheel', handler, opts);
    document.addEventListener('touchmove', handler, opts);

    this.scrollBlocker = () => {
      document.removeEventListener('wheel', handler, opts);
      document.removeEventListener('touchmove', handler, opts);
    };
  }

  private syncThemeVars(): void {
    const appRoot = document.querySelector<HTMLElement>('[data-theme-key="appRoot"]');
    const container = document.querySelector<HTMLElement>('.cdk-overlay-container');

    if (!appRoot || !container) {
      return;
    }

    for (const prop of Array.from(appRoot.style)) {
      if (prop.startsWith('--')) {
        container.style.setProperty(prop, appRoot.style.getPropertyValue(prop));
      }
    }
  }
}
