import {
  ChangeDetectionStrategy,
  Component,
  computed,
  ElementRef,
  inject,
  input,
  signal,
} from '@angular/core';
import { Highlightable } from '@angular/cdk/a11y';
import { isEqual } from 'lodash';
import { DROPDOWN_HOST, DropdownHost } from './dropdown-host';

/**
 * A selectable option component for use within a DropdownButtonComponent.
 * Implements the CDK Highlightable interface for keyboard navigation support.
 *
 * @template T - The type of value this option holds
 *
 * @example
 * ```html
 * <ui-dropdown [(ngModel)]="selected">
 *   <ui-dropdown-option [value]="'opt1'">Option 1</ui-dropdown-option>
 *   <ui-dropdown-option [value]="'opt2'">Option 2</ui-dropdown-option>
 *   <ui-dropdown-option [value]="'opt3'" [disabled]="true">Disabled</ui-dropdown-option>
 * </ui-dropdown>
 * ```
 */
@Component({
  selector: 'ui-dropdown-option',
  template: '<ng-content></ng-content>',
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: {
    '[class]':
      '["ui-dropdown-option", activeClass(), selectedClass(), disabledClass(), filteredClass()]',
    'role': 'option',
    '[attr.aria-selected]': 'selected()',
    '[attr.tabindex]': '-1',
    '(click)': 'select()',
  },
})
export class DropdownOptionComponent<T> implements Highlightable {
  /**
   * Reference to the parent dropdown component.
   * Injected from the host element to find the parent DropdownButtonComponent.
   */
  private host = inject(DROPDOWN_HOST, { host: true }) as DropdownHost<T>;

  private elementRef = inject(ElementRef);

  /**
   * Internal signal tracking the active (keyboard-highlighted) state.
   * Used by the CDK ActiveDescendantKeyManager for keyboard navigation.
   */
  active = signal(false);

  /**
   * Signal input for the disabled state.
   * Uses alias to allow getter for CDK Highlightable interface compatibility.
   * The CDK ActiveDescendantKeyManager expects a plain `disabled` boolean property,
   * but signal inputs return Signal<T>. The alias + getter pattern satisfies both.
   */
  readonly disabledInput = input<boolean>(false, { alias: 'disabled' });

  /**
   * Whether this option is disabled and cannot be selected.
   * Getter provides CDK Highlightable interface compatibility (expects plain boolean).
   */
  get disabled(): boolean {
    return this.disabledInput();
  }

  /**
   * CSS class applied when the option is disabled.
   */
  disabledClass = computed(() => (this.disabledInput() ? 'disabled' : ''));

  /**
   * CSS class applied when the option is actively highlighted via keyboard.
   */
  activeClass = computed(() => (this.active() ? 'active' : ''));

  /**
   * Sets the active (keyboard-highlighted) styles.
   * Part of the Highlightable interface for CDK keyboard navigation.
   */
  setActiveStyles(): void {
    this.active.set(true);
  }

  /**
   * Removes the active (keyboard-highlighted) styles.
   * Part of the Highlightable interface for CDK keyboard navigation.
   */
  setInactiveStyles(): void {
    this.active.set(false);
  }

  /**
   * Extracts the text label from the option's projected content.
   * Used by the parent dropdown to display the selected value.
   */
  getLabel(): string {
    return this.elementRef.nativeElement.textContent.trim();
  }

  /**
   * Computed signal indicating whether this option is currently selected.
   * Uses deep equality comparison to support object values.
   */
  selected = computed(() => {
    const hostValue = this.host.value();
    const optionValue = this.value();
    return this.host.equals()(hostValue, optionValue);
  });

  /**
   * CSS class applied when the option is selected.
   */
  selectedClass = computed(() => (this.selected() ? 'selected' : ''));

  /**
   * Whether this option is hidden by the filter.
   * Compares the filter text against the option's label (case-insensitive).
   */
  isFiltered = computed(() => {
    const filterText = this.host.filter().toLowerCase();
    if (!filterText) return false;
    return !this.getLabel().toLowerCase().includes(filterText);
  });

  /**
   * CSS class applied when the option is filtered out.
   */
  filteredClass = computed(() => (this.isFiltered() ? 'filtered' : ''));

  /**
   * The value associated with this option.
   * This is compared against the parent dropdown's value to determine selection.
   */
  value = input.required<T>();

  /**
   * Handles click events to select this option.
   * Does nothing if the option is disabled.
   */
  select(): void {
    if (this.disabled) {
      return;
    }
    this.host.select(this);
    this.host.close();
  }
}
