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

type <%= classify(name) %>Id = string;

let nextId = 0;

@Directive({
  selector: '[<%= camelize(name) %>]',
  exportAs: '<%= camelize(name) %>',
})
export class <%= classify(name) %> {
  private readonly instanceId = `<%= dasherize(name) %>-${nextId++}`;
  private readonly headers = contentChildren(<%= classify(name) %>Header, { descendants: true });

  readonly expandedIds = model<ReadonlySet<<%= classify(name) %>Id> | null>(null);

  private readonly resolvedExpandedIds = computed<ReadonlySet<<%= classify(name) %>Id>>(() => {
    const expandedIds = this.expandedIds();
    if (expandedIds) {
      return expandedIds;
    }

    return new Set(
      this.headers()
        .filter((header) => header.expanded())
        .map((header) => header.value()),
    );
  });

  isExpanded(value: <%= classify(name) %>Id): boolean {
    return this.resolvedExpandedIds().has(value);
  }

  toggle(header: <%= classify(name) %>Header): void {
    if (header.disabled()) {
      return;
    }

    const value = header.value();
    const next = new Set(this.resolvedExpandedIds());
    if (next.has(value)) {
      next.delete(value);
    } else {
      next.add(value);
    }

    this.expandedIds.set(next);
  }

  // Up/Down/Home/End move focus between headers — optional but APG-recommended keyboard support.
  navigate(current: <%= classify(name) %>Header, event: KeyboardEvent): void {
    const headers = this.headers().filter((header) => !header.disabled());
    const index = headers.indexOf(current);
    if (index === -1) {
      return;
    }

    let nextIndex: number;
    switch (event.key) {
      case 'ArrowDown':
        nextIndex = (index + 1) % headers.length;
        break;
      case 'ArrowUp':
        nextIndex = (index - 1 + headers.length) % headers.length;
        break;
      case 'Home':
        nextIndex = 0;
        break;
      case 'End':
        nextIndex = headers.length - 1;
        break;
      default:
        return;
    }

    event.preventDefault();
    headers[nextIndex]?.focus();
  }

  headerDomId(value: <%= classify(name) %>Id): string {
    return `${this.instanceId}-header-${value}`;
  }

  panelDomId(value: <%= classify(name) %>Id): string {
    return `${this.instanceId}-panel-${value}`;
  }
}

@Directive({
  selector: '[<%= camelize(name) %>Header]',
  exportAs: '<%= camelize(name) %>Header',
  host: {
    '[id]': 'root.headerDomId(value())',
    '[attr.aria-expanded]': 'open()',
    '[attr.aria-controls]': 'root.panelDomId(value())',
    '[attr.aria-disabled]': 'disabled() ? "true" : null',
    '[attr.tabindex]': 'disabled() ? -1 : null',
    '(click)': 'toggle()',
    '(keydown)': 'root.navigate(this, $event)',
  },
})
export class <%= classify(name) %>Header {
  protected readonly root = inject(<%= classify(name) %>);
  private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);

  readonly value = input.required<<%= classify(name) %>Id>();
  readonly expanded = input(false, { transform: booleanAttribute });
  readonly disabled = input(false, { transform: booleanAttribute });
  readonly open = computed(() => this.root.isExpanded(this.value()));

  toggle(): void {
    this.root.toggle(this);
  }

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

@Directive({
  selector: '[<%= camelize(name) %>Panel]',
  exportAs: '<%= camelize(name) %>Panel',
  host: {
    role: 'region',
    '[id]': 'root.panelDomId(value())',
    '[attr.aria-labelledby]': 'root.headerDomId(value())',
    '[hidden]': '!open()',
  },
})
export class <%= classify(name) %>Panel {
  protected readonly root = inject(<%= classify(name) %>);

  readonly value = input.required<<%= classify(name) %>Id>();
  readonly open = computed(() => this.root.isExpanded(this.value()));
}
