import { Component, inject, input, model } from "@angular/core";
import { BaseFilterCellComponent, FilterService, KENDO_GRID } from "@progress/kendo-angular-grid";
import { DateType, FastDatePickerComponent } from "./fast-datepicker.component";
import { Observable, tap } from "rxjs";
import { AsyncPipe } from "@angular/common";
import { FilterDescriptor, CompositeFilterDescriptor } from "@progress/kendo-data-query";
import * as util from '../utils/util';
import { MessageService } from "../services/message.service";

@Component({
  selector: 'fast-grid-date-filter-cell',
  imports: [KENDO_GRID, AsyncPipe, FastDatePickerComponent],
  template: `
    @if(filterChange$ | async) { }
    <fast-datepicker class="w-full" [(value)]="selectedDate" (valueChange)="valueChange($event)" [type]="type()"/>
  `
})
export class FastGridDateFilterCell extends BaseFilterCellComponent {
  messageService = inject(MessageService);

  filterChange$: Observable<CompositeFilterDescriptor>;
  selectedDate = model<Date>(null);
  field = input.required<string>();
  isFilterChanging: boolean = false;
  type = input.required<DateType>();

  throwTypeError() {
    util.handleError(`Type {${this.type()}} not implemented in fast-grid-col`, this.messageService);
  }

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

  valueChange(date: Date) {
    let filterDescriptor: CompositeFilterDescriptor;
    if (date)
      filterDescriptor = this.updateFilter({ field: this.field(), operator: "gte", value: date });
    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.selectedDate.set(value);
          }
          else {
            //This is to take care of the case where the selectedDate is already null but there is some partial text
            //that needs to be cleared. In that case we need to set a value first and then set it to null.
            this.selectedDate.set(new Date(1900, 0, 1));
            setTimeout(() => {
              this.selectedDate.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): Date {
    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;
  }
}
