import { Directive, ElementRef, HostListener, Input, Renderer2 } from '@angular/core';

@Directive({
  selector: '[appTooltip]',
})
export class Tooltip {
  @Input('appTooltip') text: string | null = null;
  @Input() tooltipDisabled = false;
  @Input() tooltipPlacement: 'right' | 'left' | 'top' | 'bottom' = 'right';
  @Input() tooltipOffset = 10;

  private tooltipEl?: HTMLElement;
  private removeFns: Array<() => void> = [];

  constructor(private el: ElementRef<HTMLElement>, private r: Renderer2) {}

  @HostListener('mouseenter')
  onEnter() {
    if (!this.text || this.tooltipDisabled) return;
    this.create();
    this.position();
  }

  @HostListener('mouseleave')
  onLeave() {
    this.destroy();
  }

  @HostListener('focus')
  onFocus() {
    if (!this.text || this.tooltipDisabled) return;
    this.create();
    this.position();
  }

  @HostListener('blur')
  onBlur() {
    this.destroy();
  }

  @HostListener('window:scroll')
  onScroll() {
    if (this.tooltipEl) this.position();
  }

  @HostListener('window:resize')
  onResize() {
    if (this.tooltipEl) this.position();
  }

  private create() {
    if (this.tooltipEl) return;

    const el = this.r.createElement('div');
    this.r.setStyle(el, 'position', 'fixed');
    this.r.setStyle(el, 'zIndex', '999999');
    this.r.setStyle(el, 'pointerEvents', 'none');
    this.r.setStyle(el, 'padding', '6px 10px');
    this.r.setStyle(el, 'fontSize', '12px');
    this.r.setStyle(el, 'lineHeight', '1');
    this.r.setStyle(el, 'borderRadius', '8px');
    this.r.setStyle(el, 'background', 'rgba(0,0,0,0.92)');
    this.r.setStyle(el, 'color', '#fff');
    this.r.setStyle(el, 'boxShadow', '0 8px 24px rgba(0,0,0,.28)');
    this.r.setStyle(el, 'transform', 'translate3d(0,0,0)');
    this.r.setStyle(el, 'transition', 'opacity .15s ease');
    this.r.setStyle(el, 'opacity', '0');
    this.r.setProperty(el, 'innerText', this.text ?? '');

    document.body.appendChild(el);
    // fade in next tick
    requestAnimationFrame(() => this.r.setStyle(el, 'opacity', '1'));
    this.tooltipEl = el;

    // close on escape
    const keyFn = this.r.listen('window', 'keydown', (e: KeyboardEvent) => {
      if (e.key === 'Escape') this.destroy();
    });
    this.removeFns.push(keyFn);
  }

  private position() {
    if (!this.tooltipEl) return;
    const host = this.el.nativeElement.getBoundingClientRect();
    const tip = this.tooltipEl.getBoundingClientRect();
    const offset = this.tooltipOffset;

    let top = host.top + host.height / 2 - tip.height / 2;
    let left = host.right + offset;

    switch (this.tooltipPlacement) {
      case 'left':
        left = host.left - tip.width - offset;
        break;
      case 'top':
        left = host.left + host.width / 2 - tip.width / 2;
        top = host.top - tip.height - offset;
        break;
      case 'bottom':
        left = host.left + host.width / 2 - tip.width / 2;
        top = host.bottom + offset;
        break;
      default: // right
        left = host.right + offset;
        break;
    }

    // viewport guard
    const vw = window.innerWidth;
    const vh = window.innerHeight;
    if (left + tip.width > vw - 8) left = vw - tip.width - 8;
    if (left < 8) left = 8;
    if (top + tip.height > vh - 8) top = vh - tip.height - 8;
    if (top < 8) top = 8;

    this.r.setStyle(this.tooltipEl, 'top', `${top}px`);
    this.r.setStyle(this.tooltipEl, 'left', `${left}px`);
  }

  private destroy() {
    if (this.tooltipEl?.parentNode) {
      this.tooltipEl.parentNode.removeChild(this.tooltipEl);
    }
    this.tooltipEl = undefined;
    this.removeFns.forEach((fn) => fn());
    this.removeFns = [];
  }

  ngOnDestroy() {
    this.destroy();
  }
}
