import { Component, ElementRef, Injector, afterNextRender, inject, input, signal, viewChild } from '@angular/core';

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

let nextId = 0;

@Component({
  selector: '<%= selector %>',
  imports: [],
  templateUrl: './<%= dasherize(name) %>.html',
  styleUrl: './<%= dasherize(name) %>.css',
})
export class <%= classify(name) %> {
  private readonly injector = inject(Injector);
  private readonly dialogRef = viewChild<ElementRef<HTMLElement>>('dialog');
  private triggerElement: HTMLElement | null = null;

  protected readonly instanceId = `<%= dasherize(name) %>-${nextId++}`;
  protected readonly isOpen = signal(false);

  readonly triggerLabel = input('Open dialog');
  readonly title = input('Dialog title');
  readonly description = input('Dialog content');
  readonly closeLabel = input('Close');

  protected open(event: MouseEvent): void {
    this.triggerElement = event.currentTarget as HTMLElement;
    this.isOpen.set(true);
    // Focus moves into the dialog only after Angular flushes the DOM update that un-hides it.
    afterNextRender(() => this.focusFirstElement(), { injector: this.injector });
  }

  protected close(): void {
    this.isOpen.set(false);
    this.triggerElement?.focus();
  }

  protected handleDialogKeydown(event: KeyboardEvent): void {
    if (event.key === 'Escape') {
      this.close();
      return;
    }
    if (event.key === 'Tab') {
      this.trapFocus(event);
    }
  }

  private focusFirstElement(): void {
    const dialog = this.dialogRef()?.nativeElement;
    const firstFocusable = dialog?.querySelector<HTMLElement>(FOCUSABLE_SELECTOR);
    (firstFocusable ?? dialog)?.focus();
  }

  // Keeps Tab/Shift+Tab cycling within the dialog's focusable elements (APG dialog focus trap).
  private trapFocus(event: KeyboardEvent): void {
    const dialog = this.dialogRef()?.nativeElement;
    if (!dialog) {
      return;
    }

    const focusable = Array.from(dialog.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));
    if (focusable.length === 0) {
      return;
    }

    const first = focusable[0];
    const last = focusable[focusable.length - 1];
    const active = dialog.ownerDocument.activeElement;

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