import { AfterViewInit, Component, ElementRef, HostBinding, Input, OnDestroy, ViewChild, PLATFORM_ID, inject } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { createPopper, Instance as PopperInstance } from '@popperjs/core';

@Component({
  selector: 'app-dropdown',
  template: `
    <div #targetEl [class.hidden]="!isVisible" class="z-50">
      <ng-content></ng-content>
    </div>
  `,
  standalone: true,
})
export class DropdownComponent implements AfterViewInit, OnDestroy {
  @ViewChild('targetEl') targetEl!: ElementRef<HTMLElement>;
  @Input() triggerEl!: HTMLElement;

  @Input() placement: any = 'bottom';
  @Input() triggerType: 'click' | 'hover' = 'click';
  @Input() offsetSkidding: number = 0;
  @Input() offsetDistance: number = 10;
  @Input() delay: number = 300;
  @Input() ignoreClickOutsideClass: string | false = false;

  @HostBinding('class.hidden') isHidden: boolean = true;

  private platformId = inject(PLATFORM_ID);
  private isBrowser = isPlatformBrowser(this.platformId);
  private popperInstance: PopperInstance | null = null;
  isVisible: boolean = false;
  private clickOutsideListener: any;
  private hoverShowListener: any;
  private hoverHideListener: any;

  ngAfterViewInit() {
    if (!this.isBrowser) return;

    if (!this.triggerEl) {
      console.error('Trigger element is not provided to the dropdown component.');
      return;
    }
    this.initializePopper();
    this.setupEventListeners();
  }

  ngOnDestroy() {
    this.destroyPopper();
    this.removeEventListeners();
  }

  private initializePopper() {
    this.popperInstance = createPopper(this.triggerEl, this.targetEl.nativeElement, {
      placement: this.placement,
      modifiers: [
        {
          name: 'offset',
          options: {
            offset: [this.offsetSkidding, this.offsetDistance],
          },
        },
      ],
    });
  }

  private setupEventListeners() {
    if (!this.isBrowser) return;

    if (this.triggerType === 'click') {
      this.triggerEl.addEventListener('click', () => this.toggle());
    } else if (this.triggerType === 'hover') {
      this.hoverShowListener = () => this.show();
      this.hoverHideListener = () => this.hide();
      this.triggerEl.addEventListener('mouseenter', this.hoverShowListener);
      this.triggerEl.addEventListener('mouseleave', this.hoverHideListener);
      this.targetEl.nativeElement.addEventListener('mouseenter', this.hoverShowListener);
      this.targetEl.nativeElement.addEventListener('mouseleave', this.hoverHideListener);
    }

    this.clickOutsideListener = (event: MouseEvent) => this.handleClickOutside(event);
    document.addEventListener('click', this.clickOutsideListener);
  }

  private removeEventListeners() {
    if (!this.isBrowser) return;

    if (this.triggerType === 'click') {
      this.triggerEl.removeEventListener('click', () => this.toggle());
    } else if (this.triggerType === 'hover') {
      this.triggerEl.removeEventListener('mouseenter', this.hoverShowListener);
      this.triggerEl.removeEventListener('mouseleave', this.hoverHideListener);
      this.targetEl.nativeElement.removeEventListener('mouseenter', this.hoverShowListener);
      this.targetEl.nativeElement.removeEventListener('mouseleave', this.hoverHideListener);
    }

    document.removeEventListener('click', this.clickOutsideListener);
  }

  toggle() {
    this.isVisible ? this.hide() : this.show();
  }

  show() {
    this.isVisible = true;
    this.isHidden = false;
    this.targetEl.nativeElement.classList.remove('hidden');
    this.popperInstance?.update();
  }

  hide() {
    this.isVisible = false;
    this.isHidden = true;
    this.targetEl.nativeElement.classList.add('hidden');
  }

  private handleClickOutside(event: MouseEvent) {
    if (!this.isVisible || !this.isBrowser) return;

    const clickedElement = event.target as HTMLElement;
    if (this.ignoreClickOutsideClass) {
      const ignoredElements = document.querySelectorAll(`.${this.ignoreClickOutsideClass}`);
      const arr = Array.from(ignoredElements);
      for (const el of arr) {
        if (el.contains(clickedElement)) return;
      }
    }

    if (!this.targetEl.nativeElement.contains(clickedElement) && !this.triggerEl.contains(clickedElement)) {
      this.hide();
    }
  }

  private destroyPopper() {
    if (this.popperInstance) {
      this.popperInstance.destroy();
      this.popperInstance = null;
    }
  }
}
