import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';
import { HlmComboboxImports } from '@spartan-ng/helm/combobox';
import type { Option } from '../types';

/**
 * Synchronous, searchable multi-select for many-to-many relationships with a
 * medium catalog (20–500 options) preloaded by the route resolver. Filter is
 * client-side via Spartan's default combobox filter.
 *
 * For server-side search (>500 options), see `<au-async-multiple-search-select>`.
 *
 * @example
 * <au-multiple-search-select
 *   [inputId]="'tagIds'"
 *   [value]="form.controls.tagIds.value ?? []"
 *   [options]="tagOptions()"
 *   [emptyMessage]="t('Aurora.NoResults')"
 *   (valueChange)="form.controls.tagIds.setValue($event)"
 *   (touched)="form.controls.tagIds.markAsTouched()"
 * />
 */
@Component({
  selector: 'au-multiple-search-select',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [HlmComboboxImports],
  host: { class: 'block' },
  template: `
    <hlm-combobox-multiple
      class="w-full"
      [value]="selectedOptions()"
      [disabled]="disabled()"
      [itemToString]="optionToString"
      [isItemEqualToValue]="optionEqual"
      (valueChange)="onValueChange($event)"
      (closed)="touched.emit()"
    >
      <hlm-combobox-chips class="w-full">
        <ng-template
          hlmComboboxValues
          let-values
        >
          @for (selected of values; track selected.value) {
            <hlm-combobox-chip [value]="selected">{{ selected.label }}</hlm-combobox-chip>
          }
        </ng-template>
        <input
          [id]="inputId()"
          [placeholder]="placeholder()"
          hlmComboboxChipInput
        />
      </hlm-combobox-chips>
      <hlm-combobox-content *hlmComboboxPortal>
        <hlm-combobox-empty>{{ emptyMessage() }}</hlm-combobox-empty>
        <div hlmComboboxList>
          @for (opt of options(); track opt.value) {
            <hlm-combobox-item
              [value]="opt"
              [disabled]="opt.disabled ?? false"
            >
              {{ opt.label }}
            </hlm-combobox-item>
          }
        </div>
      </hlm-combobox-content>
    </hlm-combobox-multiple>

    @if (errorMessage(); as err) {
      <p class="text-destructive mt-1 text-xs">{{ err }}</p>
    }
  `,
})
export class MultipleSearchSelect {
  readonly value = input<string[]>([]);
  readonly options = input<Option[]>([]);
  readonly inputId = input<string>('');
  readonly placeholder = input<string>('Search...');
  readonly disabled = input<boolean>(false);
  readonly emptyMessage = input<string>('No results');
  readonly errorMessage = input<string | null>(null);

  readonly valueChange = output<string[]>();
  readonly touched = output<void>();

  protected readonly selectedOptions = computed<Option[]>(() => {
    const ids = this.value();
    return this.options().filter((o) => ids.includes(o.value));
  });

  protected readonly optionToString = (opt: Option | null) => opt?.label ?? '';
  protected readonly optionEqual = (a: Option | null, b: Option | null) => a?.value === b?.value;

  protected onValueChange(options: Option[] | null): void {
    this.valueChange.emit((options ?? []).map((o) => o.value));
  }
}
