import {
  ChangeDetectionStrategy,
  Component,
  computed,
  inject,
  input,
  model,
  output,
} from '@angular/core';
import { FilterInput, FilterService } from '../../core';
import {
  Overlay,
  CdkOverlayOrigin,
  CdkConnectedOverlay,
} from '@angular/cdk/overlay';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
import { isaActionChevronDown, isaActionChevronUp } from '@isa/icons';
import { FilterInputMenuComponent } from './input-menu.component';

/**
 * A button component that toggles the visibility of an input menu for filtering.
 * It emits events when the menu is opened, closed, reset, or applied.
 * @implements {OnInit}
 */
@Component({
  selector: 'filter-input-menu-button',
  templateUrl: './input-menu-button.component.html',
  styleUrls: ['./input-menu-button.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [
    NgIconComponent,
    FilterInputMenuComponent,
    CdkOverlayOrigin,
    CdkConnectedOverlay,
  ],
  providers: [provideIcons({ isaActionChevronDown, isaActionChevronUp })],
})
export class FilterInputMenuButtonComponent {
  /** Strategy for handling scroll behavior when the overlay is open */
  scrollStrategy = inject(Overlay).scrollStrategies.block();

  /** Filter service for managing filter state */
  #filter = inject(FilterService);

  /**
   * Controls the visibility state of the input menu
   * @default false
   */
  open = model<boolean>(false);

  /**
   * The filter input configuration used to render the input menu.
   */
  filterInput = input.required<FilterInput>();

  /**
   * Emits an event when the input menu is closed.
   */
  closed = output<void>();

  /**
   * Emits an event when the input menu is opened.
   */
  opened = output<void>();

  /**
   * Emits an event when the input menu is reset.
   */
  reseted = output<void>();

  /**
   * Indicates whether the filter can be applied.
   * Defaults to false.
   * @default false
   */
  canApply = input<boolean>(false);

  /**
   * Emits an event when the input menu is applied.
   */
  applied = output<void>();

  /**
   * Determines whether to commit changes when the menu is closed.
   */
  commitOnClose = input<boolean>(false);

  /**
   * Determines whether the current input state is the default state.
   */
  isDefaultInputState = computed(() => {
    const input = this.filterInput();
    return this.#filter.isDefaultFilterInput(input);
  });

  /**
   * The label for the input menu button.
   * If not provided, it defaults to the label of the filter input.
   */
  label = input<string | undefined>(undefined);

  /**
   * Subscribes to the `applied` event to automatically close the menu.
   */
  constructor() {
    this.applied.subscribe(() => {
      this.toggle();
    });
  }

  /**
   * Toggles the visibility of the input menu.
   * Emits appropriate events based on the new state.
   * If commitOnClose is true, commits the filter changes when closing.
   */
  toggle(): void {
    const open = this.open();
    this.open.set(!open);

    if (open) {
      this.closed.emit();

      if (this.commitOnClose()) {
        this.#filter.commit();
      }
    } else {
      this.opened.emit();
    }
  }
}
