import { ChangeDetectionStrategy, Component, computed, input, OnInit, output } from '@angular/core';
import { NgIcon, provideIcons } from '@ng-icons/core';
import { lucideChevronDown } from '@ng-icons/lucide';
import { HlmButtonImports } from '@spartan-ng/helm/button';
import { HlmDialogImports } from '@spartan-ng/helm/dialog';
import { HlmSpinnerImports } from '@spartan-ng/helm/spinner';
import { HlmTableImports } from '@spartan-ng/helm/table';
import { FlexRenderDirective, type Table } from '@tanstack/angular-table';
import { useDataTable } from '../../composables/presets/use-data-table';
import { TableSearch } from '../data-table';
import type { DataTableColumnDef } from '../data-table/types';

/**
 * Many-to-one selector rendered as a table-dialog. Click the trigger button to
 * open a dialog containing the options as table rows; click a row to pick it.
 * Pure input/output — no `ControlValueAccessor`, no `FormControl` as input.
 * The host is responsible for loading `options` (optionally in response to
 * `searchChange` for server-side filtering).
 *
 * Columns use `DataTableColumnDef<T>` (the same shape as `aurora-data-table`)
 * so consumers can reuse their existing column definitions from list views.
 *
 * @example
 * <aurora-grid-select-element
 *   [inputId]="'clientId'"
 *   [value]="form.controls.clientId.value"
 *   [options]="clients()"
 *   [columns]="clientColumns"
 *   [displayWith]="displayClient"
 *   placeholder="Select client..."
 *   (valueChange)="form.controls.clientId.setValue($event)"
 *   (searchChange)="onClientSearch($event)"
 * />
 */
@Component({
  selector: 'aurora-grid-select-element',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [
    NgIcon,
    FlexRenderDirective,
    HlmButtonImports,
    HlmDialogImports,
    HlmSpinnerImports,
    HlmTableImports,
    TableSearch,
  ],
  providers: [provideIcons({ lucideChevronDown })],
  host: { class: 'block' },
  template: `
    <hlm-dialog>
      <button
        class="w-full justify-between font-normal"
        [attr.id]="inputId() || null"
        [disabled]="disabled()"
        hlmBtn
        hlmDialogTrigger
        variant="outline"
      >
        <span class="truncate">{{ triggerLabel() }}</span>
        <ng-icon
          name="lucideChevronDown"
          class="text-muted-foreground ml-2"
          hlm
          size="sm"
        />
      </button>

      <hlm-dialog-content
        *hlmDialogPortal="let ctx"
        class="sm:max-w-2xl"
      >
        <hlm-dialog-header>
          <h2 hlmDialogTitle>{{ dialogTitle() || placeholder() }}</h2>
        </hlm-dialog-header>

        <au-table-search
          [placeholder]="searchPlaceholder()"
          (search)="onSearchChange($event)"
        />

        <div class="max-h-[480px] overflow-auto rounded-md border">
          <table hlmTable>
            <thead
              class="bg-muted sticky top-0 z-10"
              hlmTHead
            >
              @for (headerGroup of table.getHeaderGroups(); track headerGroup.id) {
                <tr hlmTr>
                  @for (header of headerGroup.headers; track header.id) {
                    <th
                      [style.width.px]="header.getSize() !== 150 ? header.getSize() : null"
                      [attr.colSpan]="header.colSpan"
                      hlmTh
                    >
                      @if (!header.isPlaceholder) {
                        <ng-container
                          *flexRender="
                            header.column.columnDef.header;
                            props: header.getContext();
                            let headerText
                          "
                        >
                          <div [innerHTML]="headerText"></div>
                        </ng-container>
                      }
                    </th>
                  }
                </tr>
              }
            </thead>
            <tbody hlmTBody>
              @if (loading()) {
                <tr hlmTr>
                  <td
                    class="py-8 text-center"
                    [attr.colspan]="columns().length"
                    hlmTd
                  >
                    <hlm-spinner class="text-sm" />
                  </td>
                </tr>
              } @else {
                @for (row of table.getRowModel().rows; track row.id) {
                  <tr
                    class="hover:bg-muted/50 cursor-pointer"
                    hlmTr
                    (click)="onRowSelect(row.original, ctx)"
                  >
                    @for (cell of row.getVisibleCells(); track cell.id) {
                      <td
                        class="py-2 pr-0 pl-2"
                        hlmTd
                      >
                        <ng-container
                          *flexRender="
                            cell.column.columnDef.cell;
                            props: cell.getContext();
                            let cellText
                          "
                        >
                          <div [innerHTML]="cellText"></div>
                        </ng-container>
                      </td>
                    }
                  </tr>
                } @empty {
                  <tr hlmTr>
                    <td
                      class="h-24 text-center"
                      [attr.colspan]="columns().length"
                      hlmTd
                    >
                      {{ emptyMessage() }}
                    </td>
                  </tr>
                }
              }
            </tbody>
          </table>
        </div>
      </hlm-dialog-content>
    </hlm-dialog>
  `,
})
export class GridSelectElement<T extends { id: string }> implements OnInit {
  readonly value = input<string | null>(null);
  readonly options = input<T[]>([]);
  readonly columns = input.required<DataTableColumnDef<T>[]>();
  readonly inputId = input<string>('');
  readonly placeholder = input<string>('Select...');
  readonly loading = input<boolean>(false);
  readonly disabled = input<boolean>(false);
  readonly dialogTitle = input<string>('');
  readonly searchPlaceholder = input<string>('Filter...');
  readonly emptyMessage = input<string>('No results');
  readonly displayWith = input<(entity: T) => string>((e) => e.id);

  readonly valueChange = output<string | null>();
  readonly searchChange = output<string>();

  protected table!: Table<T>;

  protected readonly selectedEntity = computed<T | null>(() => {
    const current = this.value();
    if (current === null) return null;
    return this.options().find((o) => o.id === current) ?? null;
  });

  protected readonly triggerLabel = computed<string>(() => {
    const selected = this.selectedEntity();
    return selected ? this.displayWith()(selected) : this.placeholder();
  });

  ngOnInit(): void {
    this.table = useDataTable<T>({
      data: () => this.options(),
      columns: this.columns(),
    }).table;
  }

  protected onRowSelect(row: T, ctx: { close: () => void }): void {
    this.valueChange.emit(row.id);
    ctx.close();
  }

  protected onSearchChange(value: string): void {
    this.searchChange.emit(value);
  }
}
