import { Directive, ElementRef, AfterViewInit, OnDestroy, Input, Renderer2, OnChanges, SimpleChanges } from '@angular/core';
import { Subject } from 'rxjs';
import { debounceTime, takeUntil } from 'rxjs/operators';

@Directive({
  selector: '[appAutoFontSize]'
})
export class AutoFontSizeDirective implements AfterViewInit, OnDestroy, OnChanges {
  @Input() minFontSize: number = 12; // Mindestschriftgröße in Pixel
  @Input() maxFontSize?: number = 70; // Maximale Schriftgröße in Pixel, optional
  @Input() debounceDuration: number = 0; // Debounce-Dauer in Millisekunden
  @Input() maxFontSizePercentage: number = 30; // Maximaler Prozentsatz der Elternhöhe für die Schriftgröße

  private resizeObserver!: ResizeObserver;
  private mutationObserver!: MutationObserver;
  private resize$ = new Subject<void>();
  private destroy$ = new Subject<void>();

  private originalFontSizePx!: number;

  // Internes Property zur Validierung
  private _maxFontSizePercentage: number = 30;

  constructor(private el: ElementRef, private renderer: Renderer2) { }

  ngAfterViewInit() {
    const element: HTMLElement = this.el.nativeElement;

    // Speichere die originale Schriftgröße
    const computedStyle = window.getComputedStyle(element);
    const parsedFontSize = parseFloat(computedStyle.fontSize);
    this.originalFontSizePx = isNaN(parsedFontSize) ? 16 : parsedFontSize;

    // Set maxFontSize, falls nicht gesetzt
    const effectiveMaxFontSize = this.maxFontSize ?? this.originalFontSizePx;

    // Set initial font size
    this.renderer.setStyle(element, 'font-size', `${effectiveMaxFontSize}px`);

    // Initial Anpassung
    this.adjustFontSize();

    // Initialisiere den ResizeObserver
    this.resizeObserver = new ResizeObserver(() => {
      this.resize$.next();
    });
    this.resizeObserver.observe(element);

    // Suche das nächstgelegene Element mit der Klasse 'arrival-card'
    const arrivalCard = element.closest('.arrival-card') as HTMLElement | null;
    if (arrivalCard) {
      this.resizeObserver.observe(arrivalCard);
    }

    // Initialisiere den MutationObserver
    this.mutationObserver = new MutationObserver(() => {
      this.resize$.next();
    });
    this.mutationObserver.observe(element, {
      childList: true,
      subtree: true,
      characterData: true
    });

    // Debounce der Resize-Events, um übermäßige Aufrufe zu vermeiden
    this.resize$.pipe(
      debounceTime(this.debounceDuration),
      takeUntil(this.destroy$)
    ).subscribe(() => {
      this.adjustFontSize();
    });
  }

  ngOnChanges(changes: SimpleChanges) {
    if (changes['maxFontSizePercentage']) {
      // Re-validate und Anpassung der Schriftgröße bei Änderung
      this.validateMaxFontSizePercentage();
      this.adjustFontSize();
    }

    if (changes['maxFontSize']) {
      this.adjustFontSize();
    }

    if (changes['minFontSize']) {
      this.adjustFontSize();
    }
  }

  ngOnDestroy() {
    if (this.resizeObserver) {
      this.resizeObserver.disconnect();
    }
    if (this.mutationObserver) {
      this.mutationObserver.disconnect();
    }
    this.destroy$.next();
    this.destroy$.complete();
  }

  /**
   * Re-validate und setze den maxFontSizePercentage
   */
  private validateMaxFontSizePercentage() {
    if (this.maxFontSizePercentage < 1) {
      this._maxFontSizePercentage = 1;
    } else if (this.maxFontSizePercentage > 100) {
      this._maxFontSizePercentage = 100;
    } else {
      this._maxFontSizePercentage = this.maxFontSizePercentage;
    }
  }

  /**
   * Passt die Schriftgröße des Elements an, um Überlauf zu vermeiden und
   * sicherzustellen, dass die Schriftgröße nicht mehr als maxFontSizePercentage der Elternhöhe beträgt.
   * Verwendet eine binäre Suche zur effizienten Anpassung.
   */
  private adjustFontSize() {
    const element: HTMLElement = this.el.nativeElement;
    // Suche das nächstgelegene Element mit der Klasse 'arrival-card'
    const parent: HTMLElement | null = element.closest('.arrival-card') as HTMLElement | null;

    if (!parent) {
      return;
    }

    const parentWidth = parent.clientWidth;
    const parentHeight = parent.clientHeight;
    const elementScrollWidth = element.scrollWidth;

    // Validierung des maxFontSizePercentage
    this.validateMaxFontSizePercentage();

    // Berechne die maximale Schriftgröße basierend auf der Elternhöhe (maxFontSizePercentage%)
    const maxFontSizeByHeight = (this._maxFontSizePercentage / 100) * parentHeight;

    // Bestimme die effektive maximale Schriftgröße
    const effectiveMaxFontSize = this.maxFontSize
      ? Math.min(this.maxFontSize, maxFontSizeByHeight)
      : Math.min(this.originalFontSizePx, maxFontSizeByHeight);

    // Setze die Schriftgröße auf die maximale Größe
    this.renderer.setStyle(element, 'font-size', `${effectiveMaxFontSize}px`);

    // Überprüfe, ob der Inhalt überläuft (nur Breite)
    if (this.isWidthOverflowing(element, parent)) {
      let low = this.minFontSize;
      let high = effectiveMaxFontSize;
      let fontSize = high;

      // Binäre Suche zur Anpassung der Schriftgröße
      while (low <= high) {
        const mid = Math.floor((low + high) / 2);
        this.renderer.setStyle(element, 'font-size', `${mid}px`);

        if (this.isWidthOverflowing(element, parent)) {
          high = mid - 1;
        } else {
          fontSize = mid;
          low = mid + 1;
        }
      }

      this.renderer.setStyle(element, 'font-size', `${fontSize}px`);
 
    }

    // Zusätzliche Debugging-Informationen
    const finalFontSize = parseFloat(window.getComputedStyle(element).fontSize);
    const finalElementWidth = element.scrollWidth;
  }

  /**
   * Überprüft, ob die Breite des Elements die des Eltern-Elements überschreitet.
   * @param element Das zu überprüfende HTMLElement.
   * @param parent Das Eltern-HTMLElement.
   * @returns Wahr, wenn die Breite überläuft, sonst falsch.
   */
  private isWidthOverflowing(element: HTMLElement, parent: HTMLElement): boolean {
    const overflowing = element.scrollWidth > parent.clientWidth;
    return overflowing;
  }
}
