import { Component, PropSync, Prop, Watch, Vue } from 'vue-property-decorator';
import { QSelect } from 'quasar';

@Component({})
export default class BaseAutocompleteSelect extends Vue {
  @PropSync('model', {
    default: {
      pageSize: 20,
      lastPage: 0,
      nextPage: 1,
      search: '',
      records: [],
      selected: null,
      loading: false,
      coolDown: false,
      endOfRecords: false,
    },
  })
  modelCast: BaseAutocompleteModel;

  @Prop({ default: 'label' }) labelValue: string;
  @Prop({ default: '' }) fieldLabel: string;
  @Prop({ default: false }) isMultiple?: boolean;
  @Prop({ default: false }) disable?: boolean;
  @Prop({ default: false }) optionalSearch?: boolean;
  @Prop({ default: false }) dense?: boolean;
  
  @Prop({
    type: Function,
  })
  asyncGetter?: AsyncReturnType<() => Promise<any>>;

  @Prop({
    default: () => [],
  })
  rules: Array<(v: string) => boolean | string>;

  @Prop({ default: '' }) customPopupContentClass: string;

  $refs!: {
    select: QSelect;
  };

  @Watch('modelCast.records')
  protected recordsHandler(val: any) {
    if (val && !this.optionalSearch) {
      this.$nextTick(() => {
        this.$refs.select.focus();
        this.$refs.select.showPopup();
      });
    }
  }

  created() {
    if (this.optionalSearch) {
      this.asyncGetter().then((data: any) => {
        this.modelCast.records = data;
        this.incModel();
      });
    }
  }

  protected get opts(): any[] {
    return this.modelCast.records;
  }

  protected incModel(): void {
    this.modelCast.lastPage++;
    this.modelCast.nextPage++;
    this.modelCast.loading = false;
  }

  protected onScroll({ index, to, ref }: any) {
    if (to - index <= 5 && to >= 15) {
      if (this.asyncGetter && !this.modelCast.endOfRecords) {
        this.asyncGetter().then((data: any) => {
          if (!data.length) {
            this.modelCast.endOfRecords = true;
          }
          this.modelCast.records = [...this.modelCast.records, ...data];
          this.incModel();
          window.setTimeout(() => {
            ref.refresh();
          }, 200);
        });
      }
    }
  }

  protected onInput(value: string) {
    this.modelCast.search = value;
    if (!value && !this.optionalSearch) {
      this.modelCast.records = [];
    }
    if (this.modelCast.coolDown) {
      return;
    }
    window.setTimeout(() => {
      if (this.modelCast.search) {
        this.modelCast.lastPage = 0;
        this.modelCast.nextPage = 1;
        if (this.asyncGetter) {
          this.asyncGetter().then((data: any) => {
            this.modelCast.records = data;
            this.incModel();
          });
        }
      } else if (!this.optionalSearch) {
        this.modelCast.records = [];
      }
      this.modelCast.coolDown = false;
    }, 1500);
    this.modelCast.coolDown = true;
  }

  protected onSelect(value: any) {
    if (this.isMultiple) {
      this.$refs.select.updateInputValue('');
    }
    this.$emit('value', value);
  }
}

declare type AsyncReturnType<T extends (...args: any) => any> = T extends (
  ...args: any
) => Promise<infer U>
  ? U
  : T extends (...args: any) => infer U
  ? U
  : any;

declare interface BaseAutocompleteModel {
  pageSize: number;
  lastPage: number;
  nextPage: number;
  search: string;
  records: any[];
  selected: any;
  loading: boolean;
  coolDown: boolean;
  endOfRecords: boolean;
}
