import { Component, Emit, Prop, Watch, PropSync } from 'vue-property-decorator';
import Vue from 'vue';
import BaseSearchBar from '@components/common/base-search-bar/index.vue';
import { QPagination } from '@/models/models-ui';
import omit from 'lodash/omit';
import get from 'lodash/get';
import pick from 'lodash/pick';
import has from 'lodash/has';
import isEmpty from 'lodash/isEmpty';
import isEqual from 'lodash/isEqual';
import isFunction from 'lodash/isFunction';
import isArray from 'lodash/isArray';
import isString from 'lodash/isString';
import { TablePagination } from '@models/base/TablePagination';
import { RTableColumn, RTableSort } from '@/interfaces';

@Component({
  components: { BaseSearchBar },
  methods: {
    isEmpty,
  },
})
export default class Table extends Vue {
  isEmpty: typeof isEmpty;
  @Prop({
    type: Boolean,
    default: false,
  })
  showToggleMy?: boolean;

  @Prop() useSearch?: boolean;
  @Prop({ default: false }) hideRefresh?: boolean;
  @Prop() rowsHandler?: { (row: unknown): void };
  @Prop() rowsPerPageOptions?: number[];
  @Prop({ default: '' }) value?: string;
  @Prop() columns?: RTableColumn[];
  @Prop() sortBy?: RTableSort[];
  @Prop() data?: unknown[];
  @Prop() visibleColumns?: RTableColumn[];
  @Prop() pagination?: QPagination | null;
  @Prop() loading?: boolean;
  @Prop() title?: string;
  @Prop({
    default: false,
  })
  binarySort?: boolean;

  @Prop() selection?: string;
  @Prop({ type: Boolean, default: false }) saveQuery?: boolean;

  get columnsByName(): Record<string, RTableColumn> {
    return (
      this.columns?.reduce((cbn, column) => {
        if (column.name) cbn[column.name] = column;
        return cbn;
      }, {} as Record<string, RTableColumn>) || {}
    );
  }

  get sortableColumns(): RTableColumn[] {
    return this.columns?.filter(({ sortable }) => !!sortable) || [];
  }

  @PropSync('selected', {
    default: () => [],
  })
  syncedSelected?: unknown[];

  @PropSync('value', {
    default: '',
  })
  inputSearch: string;

  public getSortButtonClasses(target?: string): Record<string, boolean> {
    return {
      'RTable-SortBtn_CurrentSort': this.pagination?.sortBy === target,
      'RTable-SortBtn_Descending': !!this.pagination?.descending,
    };
  }

  @Emit('requestTable')
  public sortHandler(sortBy?: string): void {
    if (this.pagination) {
      if (this.pagination.sortBy !== sortBy) {
        this.pagination.sortBy = sortBy;
        this.pagination.descending = false;
      } else {
        if (!this.pagination.descending) {
          this.pagination.descending = true;
        } else {
          this.pagination.descending = true;
          this.pagination.sortBy = undefined;
        }
      }
    }
    this.updateRouteQueryString(this.pagination);
  }

  get allRowsWereSelected(): boolean {
    if (isEmpty(this.data)) return false;
    return isEqual(this.syncedSelected, this.data);
  }

  toggleSelectAll(all: boolean): void {
    this.syncedSelected = all ? this.data : [];
  }

  get rowsPerPageOptionsCustom(): number[] {
    return this.rowsPerPageOptions || [3, 5, 10, 15, 20, 25, 50];
  }

  paginationLabelCustom(pagination: TablePagination): string {
    const page = pagination.page;
    const rowsPerPage = pagination.rowsPerPage;
    const rowsNumber = pagination.rowsNumber || Number(this.data?.length);
    const firstNumber = (page - 1) * rowsPerPage + rowsPerPage;

    return `${(page - 1) * rowsPerPage + 1} - ${
      firstNumber < rowsNumber ? firstNumber : rowsNumber
    } ${this.$t('base.of').toString().toLowerCase()} ${rowsNumber}`;
  }

  private crop<T>(field: T[], start = 0, end = 2): T[] {
    return field.slice(start, end);
  }

  private difference<T>(field: T[], start = 2): T[] {
    return this.crop(field, start, field.length);
  }

  getFields(column: Partial<RTableColumn>): Partial<RTableColumn>[] {
    if (isArray(column.fields) && !isEmpty(column.fields)) return column.fields;
    return [column];
  }

  field(
    row: unknown,
    column: Partial<RTableColumn>,
    def?: unknown
  ): unknown | unknown[] {
    let field: unknown = def || '';

    if (isFunction(column.field)) {
      field = column.field(row);
    } else if (isString(column.field)) {
      field = get(row, `${column.field}`);
    }

    return field;
  }

  format(
    row: unknown,
    column: Partial<RTableColumn>,
    def?: unknown
  ): unknown | unknown[] {
    const field = this.field(row, column, def);

    let format = field;

    if (isString(column.formatField)) {
      if (isArray(field)) {
        format = field.map((item) => {
          const value = get(item, `${column.formatField}`, '');
          const defaultValue = get(item, `${column.defaultFormatField}`, '');
          return value || defaultValue;
        });
        console.log(format);
      } else {
        const value = get(field, `${column.formatField}`, '');
        const defaultValue = get(field, `${column.defaultFormatField}`, '');
        format = value || defaultValue;
      }
    } else if (isFunction(column?.format)) {
      format = column.format(field);
    }

    return format;
  }

  handler(ev: MouseEvent, row: unknown, column: Partial<RTableColumn>, item?: unknown): void {
    if (!isFunction(column.handler)) return;

    const field = this.field(row, column, item);

    column.handler(
      has(column, 'handlerField')
        ? get(field, `${column.handlerField}`)
        : field,
      {
        targetBlank: ev.metaKey || ev.ctrlKey,
      }
    );
  }

  has(column?: RTableColumn, name?: string): boolean {
    return has(column, `${name}`);
  }

  rowsHandlerCast(row: unknown): void {
    if (!!this.rowsHandler && typeof this.rowsHandler === 'function') {
      this.rowsHandler(row);
    }
  }

  @Emit('requestTable')
  requestHandler(value: any): void {
    // QTable requestServerInteraction
    // Обработка данного события сделана с изменением имени события request -> requestTable, из-за того,
    // что обычный request всплывает несколько раз для случая, когда мы перешли на каку-то страницу поиска
    // и изменили фильтр
    this.updateRouteQueryString(value.pagination);
  }

  private updateRouteQueryString(pagination: any) {
    const paramsQuery: any = pick(pagination, [
      'page',
      'sortBy',
      'descending',
      'rowsPerPage',
    ]);

    const routeQuery = { ...this.$route.query };

    Object.keys(paramsQuery).forEach((keyQuery: string) => {
      if (paramsQuery[keyQuery] === null) {
        delete paramsQuery[keyQuery];
        delete routeQuery[keyQuery];
      }
    });

    if (this.saveQuery) {
      this.$router.push({
        query: {
          ...routeQuery,
          ...paramsQuery,
        },
      });
    }
  }

  @Emit('update')
  updateHandler() {
    //
  }

  @Emit('update:pagination')
  paginationHandler() {
    //
  }

  @Emit('search')
  searchHandler() {
    //
  }

  @Emit('update:selected')
  selectedHandler() {
    //
  }

  @Emit('query')
  queryHandler() {
    //
  }
}
