import { Ref, watch } from 'vue';
import XEUtils from 'xe-utils';
import { simpleDebounce } from '/@/utils/common/compUtils';
import { JVxeDataProps, JVxeRefs, JVxeTableProps, JVxeTypes } from '../types';
import { getEnhanced } from '../utils/enhancedUtils';
import { VxeTableInstance, VxeTablePrivateMethods } from 'vxe-table';
import { cloneDeep } from 'lodash-es';
import { isArray, isEmpty, isNull, isString } from '/@/utils/is';
import { useLinkage } from './useLinkage';
import { useWebSocket } from './useWebSocket';
import { getPrefix, getJVxeAuths } from '../utils/authUtils';

export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps, refs: JVxeRefs, instanceRef: Ref) {
  let xTableTemp: VxeTableInstance & VxeTablePrivateMethods;

  function getXTable() {
    if (!xTableTemp) {
      // !. 为 typescript 的非空断言
      xTableTemp = refs.gridRef.value!.getRefMaps().refTable.value;
    }
    return xTableTemp;
  }

  // noinspection JSUnusedGlobalSymbols
  const hookMethods = {
    getXTable,
    addRows,
    pushRows,
    insertRows,
    addOrInsert,
    setValues,
    getValues,
    getTableData,
    getNewData,
    getNewDataWithId,
    getIfRowById,
    getNewRowById,
    getDeleteData,
    getSelectionData,
    getSelectedData,
    removeRows,
    removeRowsById,
    removeSelection,
    resetScrollTop,
    validateTable,
    fullValidateTable,
    clearSelection,
    filterNewRows,
    isDisabledRow,
    recalcDisableRows,
    rowResort,
  };

  // 多级联动
  const linkageMethods = useLinkage(props, data, hookMethods);
  // WebSocket 无痕刷新
  const socketMethods = useWebSocket(props, data, hookMethods);

  // 可显式供外部调用的方法
  const publicMethods = {
    ...hookMethods,
    ...linkageMethods,
    ...socketMethods,
  };

  /** 监听vxe滚动条位置 */
  function handleVxeScroll(event) {
    let { scroll } = data;

    // 记录滚动条的位置
    scroll.top = event.scrollTop;
    scroll.left = event.scrollLeft;

    refs.subPopoverRef.value?.close();
    data.scrolling.value = true;
    closeScrolling();
  }

  // 当手动勾选单选时触发的事件
  function handleVxeRadioChange(event) {
    let row = event.$table.getRadioRecord();
    data.selectedRows.value = row ? [row] : [];
    handleSelectChange('radio', data.selectedRows.value, event);
  }

  // 当手动勾选全选时触发的事件
  function handleVxeCheckboxAll(event) {
    data.selectedRows.value = event.$table.getCheckboxRecords();
    handleSelectChange('checkbox-all', data.selectedRows.value, event);
  }

  // 当手动勾选并且值发生改变时触发的事件
  function handleVxeCheckboxChange(event) {
    data.selectedRows.value = event.$table.getCheckboxRecords();
    handleSelectChange('checkbox', data.selectedRows.value, event);
  }

  // 行选择change事件
  function handleSelectChange(type, selectedRows, $event) {
    let action;
    if (type === 'radio') {
      action = 'selected';
    } else if (type === 'checkbox') {
      action = selectedRows.includes($event.row) ? 'selected' : 'unselected';
    } else {
      action = 'selected-all';
    }

    data.selectedRowIds.value = selectedRows.map((row) => row.id);
    trigger('selectRowChange', {
      type: type,
      action: action,
      $event: $event,
      row: $event.row,
      selectedRows: data.selectedRows.value,
      selectedRowIds: data.selectedRowIds.value,
    });
  }

  // 点击单元格时触发的事件
  function handleCellClick(event) {
    let { row, column, $event, $table } = event;

    // 点击了可编辑的
    if (column.editRender) {
      refs.subPopoverRef.value?.close();
      return;
    }

    // 显示详细信息
    if (column.params?.showDetails) {
      refs.detailsModalRef.value?.open(event);
    } else if (refs.subPopoverRef.value) {
      refs.subPopoverRef.value.toggle(event);
    } else if (props.clickSelectRow) {
      let className = $event.target.className || '';
      className = isString(className) ? className : className.toString();
      // 点击的是expand，不做处理
      if (className.includes('vxe-table--expand-btn')) {
        return;
      }
      // 点击的是checkbox，不做处理
      if (className.includes('vxe-checkbox--icon') || className.includes('vxe-cell--checkbox')) {
        return;
      }
      // 点击的是radio，不做处理
      if (className.includes('vxe-radio--icon') || className.includes('vxe-cell--radio')) {
        return;
      }
      if (props.rowSelectionType === 'radio') {
        $table.setRadioRow(row);
        handleVxeRadioChange(event);
      } else {
        $table.toggleCheckboxRow(row);
        handleVxeCheckboxChange(event);
      }
    }
  }

  // 单元格被激活编辑时会触发该事件
  function handleEditActived({ column }) {
    // 执行增强
    getEnhanced(column.params.type).aopEvents.editActived!.apply(instanceRef.value, arguments as any);
  }

  // 单元格编辑状态下被关闭时会触发该事件
  function handleEditClosed({ column }) {
    // 执行增强
    getEnhanced(column.params.type).aopEvents.editClosed!.apply(instanceRef.value, arguments as any);
  }

  // 返回值决定行是否可选中
  function handleCheckMethod({ row }) {
    if (props.disabled) {
      return false;
    }
    return !data.disabledRowIds.includes(row.id);
  }

  // 返回值决定单元格是否可以编辑
  function handleActiveMethod({ row, column }) {
    let flag = (() => {
      if (props.disabled) {
        return false;
      }
      if (data.disabledRowIds.includes(row.id)) {
        return false;
      }
      if (column.params?.disabled) {
        return false;
      }
      // 执行增强
      return getEnhanced(column.params.type).aopEvents.activeMethod!.apply(instanceRef.value, arguments as any) ?? true;
    })();
    if (!flag) {
      getXTable().clearActived();
    }
    return flag;
  }

  /**
   * 判断是否是禁用行
   * @param row 行数据
   * @param force 是否强制判断
   */
  function isDisabledRow(row, force = true) {
    if (!force) {
      return !data.disabledRowIds.includes(row.id);
    }
    if (props.disabledRows == null || isEmpty(props.disabledRows)) {
      return false;
    }
    let disabled: boolean = false;
    let keys: string[] = Object.keys(props.disabledRows);
    for (const key of keys) {
      // 判断是否有该属性
      if (row.hasOwnProperty(key)) {
        let temp: any = props.disabledRows![key];
        // 禁用规则可以是一个数组
        if (isArray(temp)) {
          disabled = temp.includes(row[key]);
        } else {
          disabled = temp === row[key];
        }
        if (disabled) {
          break;
        }
      }
    }
    return disabled;
  }

  // 重新计算禁用行
  function recalcDisableRows() {
    let xTable = getXTable();
    data.disabledRowIds = [];
    const { tableFullData } = xTable.internalData;
    tableFullData.forEach((row) => {
      // 判断是否是禁用行
      if (isDisabledRow(row)) {
        data.disabledRowIds.push(row.id);
      }
    });
    xTable.updateData();
  }

  // 监听 disabledRows，更改时重新计算禁用行
  watch(
    () => props.disabledRows,
    () => recalcDisableRows()
  );

  // 返回值决定是否允许展开、收起行
  function handleExpandToggleMethod({ expanded }) {
    return !(expanded && props.disabled);
  }

  // 设置 data.scrolling 防抖模式
  const closeScrolling = simpleDebounce(function () {
    data.scrolling.value = false;
  }, 100);

  /** 表尾数据处理方法，用于显示统计信息 */
  function handleFooterMethod({ columns, data: $data }) {
    const { statistics } = data;
    let footers: any[] = [];
    if (statistics.has) {
      if (statistics.sum.length > 0) {
        footers.push(
          getFooterStatisticsMap({
            columns: columns,
            title: '合计',
            checks: statistics.sum,
            method: (column) => XEUtils.sum($data, column.property),
          })
        );
      }
      if (statistics.average.length > 0) {
        footers.push(
          getFooterStatisticsMap({
            columns: columns,
            title: '平均',
            checks: statistics.average,
            method: (column) => XEUtils.mean($data, column.property),
          })
        );
      }
    }
    return footers;
  }

  /** 获取底部统计Map */
  function getFooterStatisticsMap({ columns, title, checks, method }) {
    return columns.map((column, columnIndex) => {
      if (columnIndex === 0) {
        return title;
      }
      if (checks.includes(column.property)) {
        return method(column, columnIndex);
      }
      return null;
    });
  }

  // 创建新行，自动添加默认值
  function createRow(record: Recordable = {}) {
    let xTable = getXTable();
    // 添加默认值
    xTable.internalData.tableFullColumn.forEach((column) => {
      let col = column.params;
      if (col) {
        if (col.key && (record[col.key] == null || record[col.key] === '')) {
          // 设置默认值
          let createValue = getEnhanced(col.type).createValue;
          let defaultValue = col.defaultValue ?? '';
          let ctx = { context: { row: record, column, $table: xTable } };
          record[col.key] = createValue(defaultValue, ctx);
        }
        // 处理联动列
        if (col.type === JVxeTypes.select && data.innerLinkageConfig.size > 0) {
          // 判断当前列是否是联动列
          if (data.innerLinkageConfig.has(col.key)) {
            let configItem = data.innerLinkageConfig.get(col.key);
            linkageMethods.getLinkageOptionsAsync(configItem, '');
          }
        }
      } else if (col?.type === JVxeTypes.hidden) {
        record[col.key] = col.defaultValue ?? '';
      }
    });
    return record;
  }

  async function addOrInsert(rows: Recordable | Recordable[] = {}, index, triggerName, options?: IAddRowsOptions) {
    let xTable = getXTable();
    let records;
    if (isArray(rows)) {
      records = rows;
    } else {
      records = [rows];
    }
    // 遍历添加默认值
    records.forEach((record) => createRow(record));
    let setActive = options?.setActive ?? props.addSetActive ?? true;
    let result = await pushRows(records, { index: index, setActive });
    // 遍历插入的行
    // online js增强时以传过来值为准，不再赋默认值
    if (!(options?.isOnlineJS ?? false)) {
      if (triggerName != null) {
        for (let i = 0; i < result.rows.length; i++) {
          let row = result.rows[i];
          trigger(triggerName, {
            row: row,
            rows: result.rows,
            insertIndex: index,
            $table: xTable,
            target: instanceRef.value,
            isModalData: options?.isModalData
          });
        }
      }
    }
    return result;
  }

  // 新增、插入一行时的可选参数
  interface IAddRowsOptions {
    // 是否是 onlineJS增强 触发的
    isOnlineJS?: boolean;
    // 是否激活编辑状态
    setActive?: boolean;
    //是否需要触发change事件
    emitChange?:boolean
    // 是否是modal弹窗添加的数据
    isModalData?:boolean
  }

  /**
   * 添加一行或多行
   *
   * @param rows
   * @param options 参数
   * @return
   */
  async function addRows(rows: Recordable | Recordable[] = {}, options?: IAddRowsOptions) {
    //update-begin-author:taoyan date:2022-8-12 for: VUEN-1892【online子表弹框】有主从关联js时，子表弹框修改了数据，主表字段未修改
    let result = await addOrInsert(rows, -1, 'added', options);
    if(options && options!.emitChange==true){
      trigger('valueChange', {column: 'all', row: result.row})
    }
    return result;
    //update-end-author:taoyan date:2022-8-12 for: VUEN-1892【online子表弹框】有主从关联js时，子表弹框修改了数据，主表字段未修改
  }

  /**
   * 添加一行或多行临时数据，不会填充默认值，传什么就添加进去什么
   * @param rows
   * @param options 选项
   * @param options.setActive 是否激活最后一行的编辑模式
   */
  async function pushRows(rows: Recordable | Recordable[] = {}, options = { setActive: false, index: -1 }) {
    let xTable = getXTable();
    let { setActive, index } = options;
    index = index === -1 ? index : xTable.internalData.tableFullData[index];
    // 插入行
    let result = await xTable.insertAt(rows, index);
    if (setActive) {
      // 激活最后一行的编辑模式
      xTable.setActiveRow(result.rows[result.rows.length - 1]);
    }
    await recalcSortNumber();
    return result;
  }

  /**
   * 插入一行或多行临时数据
   *
   * @param rows
   * @param index 添加下标，数字，必填
   * @param options 参数
   * @return
   */
  function insertRows(rows: Recordable | Recordable[] = {}, index: number, options?: IAddRowsOptions) {
    if (index < 0) {
      console.warn(`【JVxeTable】insertRows：index必须传递数字，且大于-1`);
      return;
    }
    return addOrInsert(rows, index, 'inserted', options);
  }

  /** 获取表格表单里的值 */
  function getValues(callback, rowIds) {
    let tableData = getTableData({ rowIds: rowIds });
    callback('', tableData);
  }

  /** 获取表格数据 */
  function getTableData(options: any = {}) {
    let { rowIds } = options;
    let tableData;
    // 仅查询指定id的行
    if (isArray(rowIds) && rowIds.length > 0) {
      tableData = [];
      rowIds.forEach((rowId) => {
        let { row } = getIfRowById(rowId);
        if (row) {
          tableData.push(row);
        }
      });
    } else {
      // 查询所有行
      tableData = getXTable().getTableData().fullData;
    }
    return filterNewRows(tableData, false);
  }

  /** 仅获取新增的数据 */
  function getNewData() {
    let newData = getNewDataWithId();
    newData.forEach((row) => delete row.id);
    return newData;
  }

  /** 仅获取新增的数据,带有id */
  function getNewDataWithId() {
    let xTable = getXTable();
    return cloneDeep(xTable.getInsertRecords());
  }

  /** 根据ID获取行，新增的行也能查出来 */
  function getIfRowById(id) {
    let xTable = getXTable();
    let row = xTable.getRowById(id),
      isNew = false;
    if (!row) {
      row = getNewRowById(id);
      if (!row) {
        console.warn(`JVxeTable.getIfRowById：没有找到id为"${id}"的行`);
        return { row: null };
      }
      isNew = true;
    }
    return { row, isNew };
  }

  /** 通过临时ID获取新增的行 */
  function getNewRowById(id) {
    let records = getXTable().getInsertRecords();
    for (let record of records) {
      if (record.id === id) {
        return record;
      }
    }
    return null;
  }

  /**
   * 过滤添加的行
   * @param rows 要筛选的行数据
   * @param remove true = 删除新增，false=只删除id
   * @param handler function
   */
  function filterNewRows(rows, remove = true, handler?: Fn) {
    let insertRecords = getXTable().getInsertRecords();
    let records: Recordable[] = [];
    for (let row of rows) {
      let item = cloneDeep(row);
      if (insertRecords.includes(row)) {
        handler ? handler({ item, row, insertRecords }) : null;
        if (remove) {
          continue;
        }
        delete item.id;
      }
      records.push(item);
    }
    return records;
  }

  /**
   * 重置滚动条Top位置
   * @param top 新top位置，留空则滚动到上次记录的位置，用于解决切换tab选项卡时导致白屏以及自动将滚动条滚动到顶部的问题
   */
  function resetScrollTop(top?) {
    let xTable = getXTable();
    xTable.scrollTo(null, top == null || top === '' ? data.scroll.top : top);
  }

  /** 校验table，失败返回errMap，成功返回null */
  async function validateTable(rows?) {
    let xTable = getXTable();
    const errMap = await xTable.validate(rows ?? true).catch((errMap) => errMap);
    return errMap ? errMap : null;
  }

  /** 完整校验 */
  async function fullValidateTable(rows?) {
    let xTable = getXTable();
    const errMap = await xTable.fullValidate(rows ?? true).catch((errMap) => errMap);
    return errMap ? errMap : null;
  }

  type setValuesParam = { rowKey: string; values: Recordable };

  /**
   * 设置某行某列的值
   *
   * @param values
   * @return 返回受影响的单元格数量
   */
  function setValues(values: setValuesParam[]): number {
    if (!isArray(values)) {
      console.warn(`[JVxeTable] setValues 必须传递数组`);
      return 0;
    }
    let xTable = getXTable();
    let count = 0;
    values.forEach((item) => {
      let { rowKey, values: record } = item;
      let { row } = getIfRowById(rowKey);
      if (!row) {
        return;
      }
      Object.keys(record).forEach((colKey) => {
        let column = xTable.getColumnByField(colKey);
        if (column) {
          let oldValue = row[colKey];
          let newValue = record[colKey];
          if (newValue !== oldValue) {
            row[colKey] = newValue;
            // 触发 valueChange 事件
            trigger('valueChange', {
              type: column.params.type,
              value: newValue,
              oldValue: oldValue,
              col: column.params,
              column: column,
              isSetValues: true,
              row: {...row}
            });
            count++;
          }
        } else {
          console.warn(`[JVxeTable] setValues 没有找到key为"${colKey}"的列`);
        }
      });
    });
    if (count > 0) {
      xTable.updateData();
    }
    return count;
  }

  /** 清空选择行 */
  async function clearSelection() {
    const xTable = getXTable();
    let event = { $table: xTable, target: instanceRef.value };
    if (props.rowSelectionType === JVxeTypes.rowRadio) {
      await xTable.clearRadioRow();
      handleVxeRadioChange(event);
    } else {
      await xTable.clearCheckboxRow();
      handleVxeCheckboxChange(event);
    }
  }

  /**
   * 获取选中数据
   * @param isFull 如果 isFull=true 则获取全表已选中的数据
   */
  function getSelectionData(isFull?: boolean) {
    const xTable = getXTable();
    if (props.rowSelectionType === JVxeTypes.rowRadio) {
      let row = xTable.getRadioRecord(isFull);
      if (isNull(row)) {
        return [];
      }
      return filterNewRows([row], false);
    } else {
      return filterNewRows(xTable.getCheckboxRecords(isFull), false);
    }
  }

  /** 仅获取被删除的数据（新增又被删除的数据不会被获取到） */
  function getDeleteData() {
    return filterNewRows(getXTable().getRemoveRecords(), false);
  }

  /** 删除一行或多行数据 */
  async function removeRows(rows) {
    const xTable = getXTable();
    const res = await xTable.remove(rows);
    let removeEvent: any = { deleteRows: rows, $table: xTable };
    trigger('removed', removeEvent);
    await recalcSortNumber();
    return res;
  }

  /** 根据id删除一行或多行 */
  function removeRowsById(rowId) {
    let rowIds;
    if (isArray(rowId)) {
      rowIds = rowId;
    } else {
      rowIds = [rowId];
    }
    let rows = rowIds
      .map((id) => {
        let { row } = getIfRowById(id);
        if (!row) {
          return;
        }
        if (row) {
          return row;
        } else {
          console.warn(`【JVxeTable】removeRowsById：${id}不存在`);
          return null;
        }
      })
      .filter((row) => row != null);
    return removeRows(rows);
  }

  // 删除选中的数据
  async function removeSelection() {
    let xTable = getXTable();
    let res;
    if (props.rowSelectionType === JVxeTypes.rowRadio) {
      res = await xTable.removeRadioRow();
    } else {
      res = await xTable.removeCheckboxRow();
    }
    await clearSelection();
    await recalcSortNumber();
    return res;
  }

  /** 重新计算排序字段的数值 */
  async function recalcSortNumber(force = false) {
    if (props.dragSort || force) {
      let xTable = getXTable();
      let sortKey = props.sortKey ?? 'orderNum';
      let sortBegin = props.sortBegin ?? 0;
      xTable.internalData.tableFullData.forEach((data) => (data[sortKey] = sortBegin++));
      // 4.1.0
      await xTable.updateCache();
      // 4.1.1
      // await xTable.cacheRowMap()
      return await xTable.updateData();
    }
  }

  /**
   * 排序表格
   * @param oldIndex
   * @param newIndex
   * @param force 强制排序
   */
  async function doSort(oldIndex: number, newIndex: number, force = false) {
    if (props.dragSort || force) {
      let xTable = getXTable();
      let sort = (array) => {
        // 存储old数据，并删除该项
        let row = array.splice(oldIndex, 1)[0];
        // 向newIndex处添加old数据
        array.splice(newIndex, 0, row);
      };
      sort(xTable.internalData.tableFullData);
      if (xTable.keepSource) {
        sort(xTable.internalData.tableSourceData);
      }
      return await recalcSortNumber(force);
    }
  }

  /** 行重新排序 */
  function rowResort(oldIndex: number, newIndex: number) {
    return doSort(oldIndex, newIndex, true);
  }

  // ---------------- begin 权限控制 ----------------
  // 加载权限
  function loadAuthsMap() {
    if (!props.authPre || props.authPre.length == 0) {
      data.authsMap.value = null;
    } else {
      data.authsMap.value = getJVxeAuths(props.authPre);
    }
  }

  /**
   * 根据 权限code 获取权限
   * @param authCode
   */
  function getAuth(authCode) {
    if (data.authsMap.value != null && props.authPre) {
      let prefix = getPrefix(props.authPre);
      return data.authsMap.value.get(prefix + authCode);
    }
    return null;
  }

  // 获取列权限
  function getColAuth(key: string) {
    return getAuth(key);
  }

  // 判断按钮权限
  function hasBtnAuth(key: string) {
    return getAuth('btn:' + key)?.isAuth ?? true;
  }

  // ---------------- end 权限控制 ----------------

  /* --- 辅助方法 ---*/

  function created() {
    loadAuthsMap();
  }

  // 触发事件
  function trigger(name, event: any = {}) {
    event.$target = instanceRef.value;
    event.$table = getXTable();
    //online增强参数兼容
    event.target = instanceRef.value;
    emit(name, event);
  }


  /**
   * 获取选中的行-和 getSelectionData 区别在于对于新增的行也会返回ID
   * 用于onlinePopForm
   * @param isFull
   */
  function getSelectedData(isFull?: boolean) {
    const xTable = getXTable();
    let rows:any[] = []
    if (props.rowSelectionType === JVxeTypes.rowRadio) {
      let row = xTable.getRadioRecord(isFull);
      if (isNull(row)) {
        return [];
      }
      rows = [row]
    } else {
      rows = xTable.getCheckboxRecords(isFull)
    }
    let records: Recordable[] = [];
    for (let row of rows) {
      let item = cloneDeep(row);
      records.push(item);
    }
    return records;
  }

  return {
    methods: {
      trigger,
      ...publicMethods,
      closeScrolling,
      doSort,
      recalcSortNumber,
      handleVxeScroll,
      handleVxeRadioChange,
      handleVxeCheckboxAll,
      handleVxeCheckboxChange,
      handleFooterMethod,
      handleCellClick,
      handleEditActived,
      handleEditClosed,
      handleCheckMethod,
      handleActiveMethod,
      handleExpandToggleMethod,
      getColAuth,
      hasBtnAuth,
    },
    publicMethods,
    created,
  };
}
