import {
  AfterViewInit,
  ChangeDetectionStrategy,
  Component,
  ElementRef,
  HostListener,
  input,
  OnChanges,
  output,
  SimpleChanges,
  ViewChild
} from '@angular/core';
import { TranslatePipe } from '../../../i18n/translate.pipe';

export type DialogSize = 'sm' | 'md' | 'lg';

const FOCUSABLE_SELECTOR =
  'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), iframe, [tabindex]:not([tabindex="-1"])';

@Component({
  selector: 'app-dialog',
  standalone: true,
  imports: [TranslatePipe],
  templateUrl: './dialog.component.html',
  styleUrl: './dialog.component.scss',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class DialogComponent implements OnChanges, AfterViewInit {
  readonly open = input(false);
  readonly titleText = input<string | null>(null);
  // Accessible name for dialogs that skip the visible titled header (a
  // consumer-rendered close button inside the content instead). Ignored
  // when titleText is set - the header already supplies the name.
  readonly ariaLabel = input<string | null>(null);
  readonly size = input<DialogSize>('md');
  // Both default true (existing behavior for every current consumer).
  readonly closeOnEscape = input(true);
  readonly closeOnBackdropClick = input(true);

  readonly closed = output<void>();

  @ViewChild('panel') private panelRef?: ElementRef<HTMLElement>;

  private previouslyFocused: HTMLElement | null = null;

  ngOnChanges(changes: SimpleChanges): void {
    if (changes['open']) {
      if (this.open()) {
        this.previouslyFocused = document.activeElement as HTMLElement | null;
        queueMicrotask(() => this.focusPanel());
      } else {
        this.previouslyFocused?.focus();
        this.previouslyFocused = null;
      }
    }
  }

  ngAfterViewInit(): void {
    if (this.open()) {
      this.focusPanel();
    }
  }

  @HostListener('document:keydown', ['$event'])
  protected handleKeydown(event: KeyboardEvent): void {
    if (!this.open()) {
      return;
    }
    if (event.key === 'Escape') {
      if (this.closeOnEscape()) {
        this.requestClose();
      }
      return;
    }
    if (event.key === 'Tab') {
      this.trapFocus(event);
    }
  }

  protected requestClose(): void {
    this.closed.emit();
  }

  protected handleBackdropClick(): void {
    if (this.closeOnBackdropClick()) {
      this.requestClose();
    }
  }

  private focusPanel(): void {
    const panel = this.panelRef?.nativeElement;
    if (!panel) {
      return;
    }
    const focusable = panel.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR);
    (focusable[0] ?? panel).focus();
  }

  private trapFocus(event: KeyboardEvent): void {
    const panel = this.panelRef?.nativeElement;
    if (!panel) {
      return;
    }
    const focusable = Array.from(panel.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));
    if (focusable.length === 0) {
      return;
    }
    const first = focusable[0];
    const last = focusable[focusable.length - 1];
    const active = document.activeElement;

    if (event.shiftKey && active === first) {
      event.preventDefault();
      last.focus();
    } else if (!event.shiftKey && active === last) {
      event.preventDefault();
      first.focus();
    }
  }
}
