import { sanitizeString } from '@/helpers/string.helper';
import { Sort, SortValue, TableHeader } from '@/models';
import { Ref, onMounted, ref, unref, watch } from 'vue';
import _get from 'lodash/get';
import {
  getStringFromPathQuery,
  saveValuesToPathQuery,
} from '@/helpers/location.helper';

const SORT_TARGET_KEY = 'table-sort-target';
const SORT_VALUE_KEY = 'table-sort-value';

export const useSort = (
  persistent: boolean | Ref<boolean> = true,
  tableHeaders: TableHeader[] | Ref<TableHeader[]> = [],
  defaultSort: Sort | Ref<Sort> = {
    key: null,
    value: SortValue.default,
    overridenSortFunction: undefined,
  },
) => {
  const reflect = ref<boolean>(unref(persistent));
  const headers = ref<TableHeader[]>(unref(tableHeaders));
  const sort: Ref<Sort> = ref(unref(defaultSort));
  const isMounting: Ref<boolean> = ref(true);

  const changeSort = (header: TableHeader) => {
    if (!header.sortable) {
      return;
    }
    if (sort.value.key !== header.key) {
      sort.value = {
        key: header.key,
        value: SortValue.asc,
        overridenSortFunction: header.sortFunction || undefined,
      };
    } else {
      sort.value.value =
        sort.value.value === SortValue.asc ? SortValue.desc : SortValue.asc;
    }
  };

  const sortItems = (items: any[]): any[] => {
    if (!sort.value.key || sort.value.value === SortValue.default) {
      return items;
    }
    return items.sort((a: any, b: any) => {
      if (sort.value.overridenSortFunction) {
        return sort.value.overridenSortFunction(a, b, sort.value.value);
      }
      if (sort.value.key) {
        return defaultSortFunction(
          _get(a, sort.value.key),
          _get(b, sort.value.key),
          sort.value.value,
        );
      }
      return 0;
    });
  };

  onMounted(() => {
    if (reflect.value && isSortPresentInPath()) {
      const target = getStringFromPathQuery(SORT_TARGET_KEY);
      const requestedSortValue = getStringFromPathQuery(SORT_VALUE_KEY);
      const foundHeader = headers.value.find((header) => header.key === target);
      if (
        foundHeader &&
        Object.values(SortValue).includes(requestedSortValue as SortValue)
      ) {
        sort.value = {
          key: target,
          value: requestedSortValue as SortValue,
          overridenSortFunction: foundHeader.sortFunction || undefined,
        };
      }
    }
    isMounting.value = false;
  });

  const isSortPresentInPath = (): boolean => {
    return (
      !!getStringFromPathQuery(SORT_TARGET_KEY) &&
      !!getStringFromPathQuery(SORT_VALUE_KEY)
    );
  };

  watch(
    () => sort.value,
    () => {
      if (reflect.value && sort.value.key) {
        saveValuesToPathQuery([
          { key: SORT_TARGET_KEY, value: sort.value.key },
          { key: SORT_VALUE_KEY, value: sort.value.value },
        ]);
      }
    },
    { deep: true },
  );

  return {
    sort,
    isMounting,
    changeSort,
    sortItems,
    isSortPresentInPath,
  };
};

export const defaultSortFunction = (
  valA: any,
  valB: any,
  value: SortValue,
): number => {
  if (typeof valA === 'string') {
    valA = sanitizeString(valA);
  }
  if (typeof valB === 'string') {
    valB = sanitizeString(valB);
  }

  if (valA < valB || (valA === undefined && valB !== undefined)) {
    return value === SortValue.asc ? -1 : 1;
  }
  if (valA > valB || (valA !== undefined && valB === undefined)) {
    return value === SortValue.asc ? 1 : -1;
  }
  return 0;
};
