import { NgTemplateOutlet } from '@angular/common';
import {
  Component,
  Directive,
  TemplateRef,
  booleanAttribute,
  computed,
  contentChildren,
  inject,
  input,
  signal,
} from '@angular/core';

type <%= classify(name) %>SlideId = string | number;

let nextId = 0;
let nextSlideId = 0;

@Directive({
  selector: 'ng-template[<%= camelize(name) %>Slide]',
})
export class <%= classify(name) %>Slide {
  readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);

  readonly id = input<<%= classify(name) %>SlideId>(`<%= dasherize(name) %>-slide-${nextSlideId++}`);
  readonly label = input('');
}

@Component({
  selector: '<%= selector %>',
  imports: [NgTemplateOutlet],
  templateUrl: './<%= dasherize(name) %>.html',
  styleUrl: './<%= dasherize(name) %>.css',
})
export class <%= classify(name) %> {
  protected readonly slides = contentChildren(<%= classify(name) %>Slide);
  protected readonly instanceId = `<%= dasherize(name) %>-${nextId++}`;

  readonly label = input('Featured items');
  readonly previousLabel = input('Previous slide');
  readonly nextLabel = input('Next slide');
  readonly preserveContent = input(true, { transform: booleanAttribute });

  protected readonly activeIndex = signal(0);

  private readonly renderedIds = signal<ReadonlySet<<%= classify(name) %>SlideId>>(new Set());
  private readonly slideCount = computed(() => this.slides().length);
  private readonly activeSlide = computed(() => this.slides()[this.activeIndex()] ?? null);

  protected isActive(index: number): boolean {
    return this.activeIndex() === index;
  }

  protected shouldRender(slide: <%= classify(name) %>Slide): boolean {
    return this.activeSlide()?.id() === slide.id() || (this.preserveContent() && this.renderedIds().has(slide.id()));
  }

  protected slideLabel(index: number, slide: <%= classify(name) %>Slide): string {
    const position = `${index + 1} of ${this.slideCount()}`;
    return slide.label() ? `${position}: ${slide.label()}` : position;
  }

  protected previous(): void {
    this.selectIndex(this.activeIndex() - 1);
  }

  protected next(): void {
    this.selectIndex(this.activeIndex() + 1);
  }

  private selectIndex(index: number): void {
    const slides = this.slides();
    if (slides.length === 0) {
      return;
    }

    const activeSlide = this.activeSlide();
    if (activeSlide) {
      this.markRendered(activeSlide.id());
    }

    const nextIndex = (index + slides.length) % slides.length;
    this.markRendered(slides[nextIndex].id());
    this.activeIndex.set(nextIndex);
  }

  private markRendered(id: <%= classify(name) %>SlideId): void {
    const renderedIds = this.renderedIds();
    if (renderedIds.has(id)) {
      return;
    }

    this.renderedIds.set(new Set(renderedIds).add(id));
  }
}
