import {CommonModule} from '@angular/common';
import {
  AfterContentInit,
  booleanAttribute,
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  ContentChild,
  ElementRef,
  EventEmitter,
  forwardRef,
  inject,
  Input,
  Output,
  TemplateRef,
  ViewChild,
  ViewEncapsulation
} from '@angular/core';
import {ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR} from '@angular/forms';
import {EngieTemplateDirective} from '../../directives/engie-template.directive';
import {sizeVariantToCSS, TSizeVariants} from '../../models/size-variant.model';
import {Utils} from '../../utils/utils.util';
import {IconButtonComponent} from '../icon-button/icon-button.component';
import {IconComponent} from '../icon/icon.component';

@Component({
  selector: 'nj-search',
  templateUrl: './search.component.html',
  styleUrls: ['./search.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  standalone: true,
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => SearchComponent),
    multi: true
  }],
  imports: [
    CommonModule,
    IconComponent,
    IconButtonComponent,
    FormsModule,
  ],
  host: {
    '[class]': 'classes'
  }
})
export class SearchComponent implements AfterContentInit, ControlValueAccessor {
  private readonly defaultInputId = Utils.getUID('nj-search-input');
  private cdr = inject(ChangeDetectorRef);
  private isDisabledByForm = false;
  private onChange: ((value: string | null) => void) | null = null;

  protected value: string | null = null;
  protected onTouched: (() => void) | null = null;
  protected actionTemplate?: TemplateRef<unknown>;

  @ViewChild('searchInput') private searchInputElement?: ElementRef<HTMLInputElement>;

  /**
   * Specifies whether the element is disabled or not.
   */
  @Input({transform: booleanAttribute}) disabled= false;

  /**
   * HTML input id
   */
  @Input() inputId?: string;

  /**
   * Placeholder input text.
   */
  @Input() placeholder?: string;

  /**
   * The size of the search component.
   */
  @Input() scale?: Extract<TSizeVariants, 'sm' | 'md' | 'lg' | 'xl'>;

  /**
   * Reset button alternative label for assistive technologies
   */
  @Input() altResetButtonLabel?: string;

  /**
   * Alternative label for assistive technologies
   */
  @Input() altLabel?: string;

  /**
   * Input keyboard keydown events.
   */
  @Output() enterKeydown = new EventEmitter<void>();

  @ContentChild(EngieTemplateDirective) protected element?: EngieTemplateDirective;

  ngAfterContentInit() {
    if (this.element?.selector === 'action') {
      this.actionTemplate = this.element.templateRef;
    }
  }

  protected get classes() {
    const classes = ['nj-search'];
    if (this.scale) {
      classes.push(`nj-search--${sizeVariantToCSS(this.scale)}`);
    }

    return classes;
  }

  protected get isDisabled() {
    return this.disabled || this.isDisabledByForm;
  }

  protected get finalInputId() {
    return this.inputId || this.defaultInputId;
  }

  protected valueChange(value: string)  {
    this.value = value;
    this.onChange?.(value);
  }

  protected onClearValue()  {
    this.value = null;
    this.onChange?.(this.value);
    this.searchInputElement?.nativeElement.focus();
  }

  //<editor-fold desc="ControlValueAccessor implementation">
  /**
   * @ignore
   */
  writeValue(value: string): void {
    if (this.value === value) {
      return;
    }
    this.value = value;
    this.cdr.markForCheck();
  }

  /**
   * @ignore
   */
  registerOnChange(onChange: (value: string | null) => void): void {
    this.onChange = onChange;
  }

  /**
   * @ignore
   */
  registerOnTouched(onTouched: () => void): void {
    this.onTouched = onTouched;
  }

  /**
   * @ignore
   */
  setDisabledState(isDisabled: boolean): void {
    this.isDisabledByForm = isDisabled;
  }
  //</editor-fold>
}
