import { Component, ElementRef, Injector, afterNextRender, booleanAttribute, computed, 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"])';

type <%= classify(name) %>Placement = 'start' | 'end' | 'top' | 'bottom';

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 panelRef = viewChild<ElementRef<HTMLElement>>('panel');
  private triggerElement: HTMLElement | null = null;

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

  readonly triggerLabel = input('Open panel');
  readonly title = input('Panel title');
  readonly description = input('Use this panel for secondary actions, filters, or contextual content.');
  readonly closeLabel = input('Close panel');
  readonly placement = input<<%= classify(name) %>Placement>('end');
  readonly backdrop = input(true, { transform: booleanAttribute });
  readonly closeOnBackdrop = input(true, { transform: booleanAttribute });

  protected readonly panelClasses = computed(() => [
    '<%= dasherize(name) %>__panel',
    `<%= dasherize(name) %>__panel--${this.placement()}`,
  ].join(' '));

  protected open(event: MouseEvent): void {
    this.triggerElement = event.currentTarget as HTMLElement;
    this.isOpen.set(true);
    afterNextRender(() => this.focusFirstElement(), { injector: this.injector });
  }

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

  protected handleBackdropClick(): void {
    if (this.closeOnBackdrop()) {
      this.close();
    }
  }

  protected handlePanelKeydown(event: KeyboardEvent): void {
    if (event.key === 'Escape') {
      event.preventDefault();
      this.close();
      return;
    }

    if (event.key === 'Tab') {
      this.trapFocus(event);
    }
  }

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

  // Keeps Tab/Shift+Tab cycling within the offcanvas while it behaves as a modal dialog.
  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 = panel.ownerDocument.activeElement;

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