import { Directive, ElementRef, booleanAttribute, computed, contentChildren, inject, input, model } from '@angular/core';

type <%= classify(name) %>Orientation = 'vertical' | 'horizontal';

@Directive({
  selector: '[<%= camelize(name) %>]',
  exportAs: '<%= camelize(name) %>',
  host: {
    role: 'list',
    '[attr.aria-label]': 'label() || null',
    '[attr.aria-orientation]': 'orientation()',
    '(keydown)': 'handleKeydown($event)',
  },
})
export class <%= classify(name) %> {
  private readonly items = contentChildren(<%= classify(name) %>Item, { descendants: true });

  readonly label = input('');
  readonly orientation = input<<%= classify(name) %>Orientation>('vertical');
  readonly selectable = input(true, { transform: booleanAttribute });
  readonly activeId = model<string | null>(null);

  isActive(item: <%= classify(name) %>Item): boolean {
    return this.activeId() === item.value();
  }

  select(item: <%= classify(name) %>Item): void {
    if (!this.selectable() || item.disabled()) {
      return;
    }

    this.activeId.set(item.value());
  }

  handleKeydown(event: KeyboardEvent): void {
    const enabledItems = this.items().filter((item) => !item.disabled());
    const currentIndex = enabledItems.findIndex((item) => this.isActive(item));
    const nextKey = this.orientation() === 'horizontal' ? 'ArrowRight' : 'ArrowDown';
    const previousKey = this.orientation() === 'horizontal' ? 'ArrowLeft' : 'ArrowUp';

    if (event.key === nextKey) {
      event.preventDefault();
      this.focusAt(enabledItems, currentIndex + 1);
      return;
    }

    if (event.key === previousKey) {
      event.preventDefault();
      this.focusAt(enabledItems, currentIndex - 1);
      return;
    }

    if (event.key === 'Home') {
      event.preventDefault();
      this.focusAt(enabledItems, 0);
      return;
    }

    if (event.key === 'End') {
      event.preventDefault();
      this.focusAt(enabledItems, enabledItems.length - 1);
      return;
    }

    if (event.key === ' ' || event.key === 'Enter') {
      const activeItem = enabledItems[currentIndex];
      if (activeItem) {
        event.preventDefault();
        this.select(activeItem);
      }
    }
  }

  private focusAt(items: readonly <%= classify(name) %>Item[], index: number): void {
    if (!items.length) {
      return;
    }

    const nextIndex = (index + items.length) % items.length;
    const item = items[nextIndex];
    this.activeId.set(item.value());
    item.focus();
  }
}

@Directive({
  selector: '[<%= camelize(name) %>Item]',
  exportAs: '<%= camelize(name) %>Item',
  host: {
    role: 'listitem',
    '[attr.aria-current]': 'active() ? ariaCurrent() : null',
    '[attr.aria-disabled]': 'disabled() ? "true" : null',
    '[attr.tabindex]': 'disabled() ? -1 : active() ? 0 : -1',
    '(click)': 'select()',
  },
})
export class <%= classify(name) %>Item {
  private readonly listGroup = inject(<%= classify(name) %>);
  private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);

  readonly value = input.required<string>();
  readonly disabled = input(false, { transform: booleanAttribute });
  readonly ariaCurrent = input('true');
  readonly active = computed(() => this.listGroup.isActive(this));

  select(): void {
    this.listGroup.select(this);
  }

  focus(): void {
    this.elementRef.nativeElement.focus();
  }
}
