import { Component, computed, effect, inject, input, model } from "@angular/core";
import { BaseFilterCellComponent, FilterService, KENDO_GRID } from "@progress/kendo-angular-grid";
import { Observable, tap } from "rxjs";
import { AsyncPipe } from "@angular/common";
import { FilterDescriptor, CompositeFilterDescriptor } from "@progress/kendo-data-query";
import { MessageService } from "../services/message.service";
import { FastNumericTextBoxComponent } from "./fast-numerictextbox.component";

@Component({
  selector: 'fast-grid-numeric-filter-cell',
  imports: [KENDO_GRID, AsyncPipe, FastNumericTextBoxComponent],
  template: `
    @if(filterChange$ | async) { }
    <fast-numerictextbox class="w-full" [spinners]="false" [step]="0" [(value)]="inputValue" (valueChange)="valueChange($event)"
      [decimals]="getDecimals()" [format]="format()" />
  `
})
export class FastGridNumericFilterCell extends BaseFilterCellComponent {
  messageService = inject(MessageService);

  filterChange$: Observable<CompositeFilterDescriptor>;
  inputValue = model<number>(null);
  field = input.required<string>();
  isFilterChanging: boolean = false;
  type = input.required<string>();
  format = input<string>();
  columnFilter = input<CompositeFilterDescriptor | FilterDescriptor>(null);

  getDecimals = computed(() => {
    const type = this.type;

    if (['num-d0', 'num-d0-edit', 'num-c0'].includes(type())) return 0;
    if (['num-d2', 'num-d2-edit', 'num-c2'].includes(type())) return 2;
    if (['num-d5', 'num-c5', 'num-c5-edit'].includes(type())) return 5;
    if (['num-p#*100'].includes(type())) return 5;

    return 0;
  });

  constructor() {
    const filterService = inject(FilterService);
    super(filterService);

    effect(() => {
      const filter = this.columnFilter();
      this.isFilterChanging = true;
      const value = filter ? this.findFilterValueByField(filter, this.field()) : null;

      if (value !== null) {
        this.inputValue.set(value);
        this.isFilterChanging = false;
      }
      else {
        //This is to take care of the case where the inputValue is already null but there is some number
        //that needs to be cleared. In that case we need to set a value first and then set it to null.
        this.inputValue.set(0);
        setTimeout(() => {
          this.inputValue.set(null);
          this.isFilterChanging = false;
        });
      }
    });
  }

  valueChange(inputValue: number | null) {
    if (this.isFilterChanging) return;

    let filterDescriptor: CompositeFilterDescriptor;
    if (inputValue)
      filterDescriptor = this.updateFilter({ field: this.field(), operator: "eq", value: inputValue });
    else
      filterDescriptor = this.removeFilter(this.field());

    this.applyFilter(filterDescriptor);
  }

  public setFilterService(filterService: FilterService) {
    //this sets filterService on the inherited BaseFilterCellComponent
    //the injected one in the constructor does not work but is needed to compile
    //setFilterService must be called and passed the filterService from the kendo-grid
    this.filterService = filterService;
    this.filterChange$ = this.filterService.changes.pipe(
      tap(change => {
        setTimeout(() => {
          this.isFilterChanging = true;
          if (change?.filters?.length >= 1) {
            const filter = change.filters[0];
            const value = this.findFilterValueByField(filter, this.field());
            this.inputValue.set(value);
          }
          else {
            //This is to take care of the case where the inputValue is already null but there is some number
            //that needs to be cleared. In that case we need to set a value first and then set it to null.
            this.inputValue.set(0);
            setTimeout(() => {
              this.inputValue.set(null);
            });
          }
          this.isFilterChanging = false;
        });
      })
    );
  }

  private isCompositeFilterDescriptor(filter: CompositeFilterDescriptor | FilterDescriptor): filter is CompositeFilterDescriptor {
    return filter && typeof filter === 'object' && 'logic' in filter && 'filters' in filter;
  }

  private isFilterDescriptor(filter: CompositeFilterDescriptor | FilterDescriptor): filter is FilterDescriptor {
    return filter && typeof filter === 'object' && 'field' in filter && 'operator' in filter;
  }

  private findFilterValueByField(filter: CompositeFilterDescriptor | FilterDescriptor, fieldName: string): number {
    if (this.isCompositeFilterDescriptor(filter)) {
      // Recursively search through composite filters
      for (const subFilter of filter.filters) {
        const value = this.findFilterValueByField(subFilter, fieldName);
        if (value !== null) {
          return value;
        }
      }
      return null;
    } else if (this.isFilterDescriptor(filter)) {
      // Check if this filter matches the field we're looking for
      return filter.field === fieldName ? filter.value : null;
    }
    return null;
  }
}
