import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
/* eslint-disable no-plusplus */

import { useEffect, useMemo, useState, useRef, useImperativeHandle, forwardRef, useContext } from 'react';
import clsx from 'clsx';
import Loading from '../loading';
import Column from './PickerColumn';
import useRefs from '../hooks/use-refs';
import useEventListener from '../hooks/use-event-listener';
import { unitToPx, preventDefault, extend } from '../utils';
import { BORDER_UNSET_TOP_BOTTOM } from '../utils/constant';
import ConfigProviderContext from '../config-provider/ConfigProviderContext';
import { raf } from '../utils/raf';

function PickerInner(props, ref) {
  const {
    prefixCls,
    createNamespace,
    locale
  } = useContext(ConfigProviderContext);
  const [bem] = createNamespace('picker', prefixCls);
  const [refs, setRefs, resetRefs] = useRefs();
  const [formattedColumns, setFormattedColumns] = useState([]);
  const {
    text: textKey,
    values: valuesKey,
    children: childrenKey
  } = extend({
    // compatible with valueKey prop
    text: 'text',
    values: 'values',
    children: 'children'
  }, props.columnsFieldNames);
  const wrapper = useRef(null);
  const itemHeight = useMemo(() => unitToPx(props.itemHeight), [props.itemHeight]);
  const dataType = useMemo(() => {
    const {
      columns
    } = props;
    const firstColumn = columns[0] || {};

    if (typeof firstColumn === 'object') {
      if (childrenKey in firstColumn) {
        return 'cascade';
      }

      if (valuesKey in firstColumn) {
        return 'object';
      }
    }

    return 'plain'; // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [props.columns]);

  const formatCascade = () => {
    var _a;

    const formatted = [];
    let cursor = {
      [childrenKey]: props.columns
    };

    while (cursor && cursor[childrenKey]) {
      const children = cursor[childrenKey];
      let defaultIndex = (_a = cursor.defaultIndex) !== null && _a !== void 0 ? _a : +props.defaultIndex;

      while (children[defaultIndex] && children[defaultIndex].disabled) {
        if (defaultIndex < children.length - 1) {
          defaultIndex++;
        } else {
          defaultIndex = 0;
          break;
        }
      }

      formatted.push({
        [valuesKey]: cursor[childrenKey],
        className: cursor.className,
        defaultIndex
      });
      cursor = children[defaultIndex];
    }

    setFormattedColumns(formatted);
  };

  const format = () => {
    const {
      columns
    } = props;

    if (dataType === 'plain') {
      setFormattedColumns([{
        [valuesKey]: columns
      }]);
    } else if (dataType === 'cascade') {
      formatCascade();
    } else {
      setFormattedColumns(columns);
    }
  }; // get indexes of all columns


  const getIndexes = () => refs.map(_ref => _ref.state.index); // set options of column by index


  const setColumnValues = (index, options) => {
    const column = refs[index];

    if (column) {
      column.setOptions(options);
    }
  };

  const onCascadeChange = columnIndex => {
    var _a;

    let cursor = {
      [childrenKey]: props.columns
    };
    const indexes = getIndexes();

    for (let i = 0; i <= columnIndex; i += 1) {
      cursor = cursor[childrenKey][indexes[i]];
    }

    if (cursor && cursor[childrenKey]) {
      while (cursor && cursor[childrenKey]) {
        columnIndex += 1;
        setColumnValues(columnIndex, cursor[childrenKey]);
        cursor = (_a = cursor[childrenKey]) === null || _a === void 0 ? void 0 : _a[cursor.defaultIndex || 0];
      }
    } else {
      // Clean unsafe data children value
      // https://github.com/3lang3/react-vant/issues/378
      refs.forEach((column, i) => {
        if (i > columnIndex && columnIndex < refs.length) {
          column.setOptions([]);
        }
      });
    }
  }; // get column instance by index


  const getChild = index => refs[index]; // get column value by index


  const getColumnValue = index => {
    const column = getChild(index);
    return column.getValue();
  }; // set column value by index


  const setColumnValue = (index, value) => {
    const column = getChild(index);

    if (column) {
      column.setValue(value);

      if (dataType === 'cascade') {
        onCascadeChange(index);
      }
    }
  }; // get column option index by column index


  const getColumnIndex = index => {
    const column = getChild(index);
    return column.state.index;
  }; // set column option index by column index


  const setColumnIndex = (columnIndex, optionIndex) => {
    const column = getChild(columnIndex);

    if (column) {
      column.setIndex(optionIndex);

      if (dataType === 'cascade') {
        onCascadeChange(columnIndex);
      }
    }
  }; // get options of column by index


  const getColumnValues = index => {
    const column = getChild(index);
    return column && column.getValue();
  }; // get values of all columns


  const getValues = () => refs.map(_ref => _ref.getValue()); // set values of all columns


  const setValues = values => {
    values.forEach((value, index) => {
      setColumnValue(index, value);
    });
  }; // set indexes of all columns


  const setIndexes = indexes => {
    indexes.forEach((optionIndex, columnIndex) => {
      setColumnIndex(columnIndex, optionIndex);
    });
  };

  const onChange = columnIndex => {
    if (dataType === 'cascade') {
      onCascadeChange(columnIndex);
    }

    if (props.onChange) {
      if (dataType === 'plain') {
        props.onChange(getColumnValue(0), getColumnIndex(0));
      } else {
        props.onChange(getValues(), columnIndex);
      }
    }
  };

  const confirm = () => {
    refs.forEach(_ref => _ref.stopMomentum());

    if (props.onConfirm) {
      raf(() => {
        if (dataType === 'plain') {
          props.onConfirm(getColumnValue(0), getColumnIndex(0));
        } else {
          props.onConfirm(getValues(), getIndexes());
        }
      });
    }
  };

  const cancel = () => {
    var _a, _b;

    if (dataType === 'plain') {
      (_a = props.onCancel) === null || _a === void 0 ? void 0 : _a.call(props, getColumnValue(0), getColumnIndex(0));
    } else {
      (_b = props.onCancel) === null || _b === void 0 ? void 0 : _b.call(props, getValues(), getIndexes());
    }
  };

  const renderTitle = () => {
    if (props.title) {
      return _jsx("div", Object.assign({
        className: clsx(bem('title'), 'rv-ellipsis')
      }, {
        children: props.title
      }));
    }

    return null;
  };

  const renderCancel = () => {
    const text = props.cancelButtonText || locale.cancel;
    return _jsx("button", Object.assign({
      type: "button",
      className: clsx(bem('cancel')),
      onClick: cancel
    }, {
      children: text
    }));
  };

  const renderConfirm = () => {
    const text = props.confirmButtonText || locale.confirm;
    return _jsx("button", Object.assign({
      type: "button",
      className: clsx(bem('confirm')),
      onClick: confirm
    }, {
      children: text
    }));
  };

  const renderToolbar = () => {
    if (props.showToolbar) {
      return _jsx("div", Object.assign({
        className: clsx(bem('toolbar'))
      }, {
        children: props.toolbar || _jsxs(_Fragment, {
          children: [renderCancel(), renderTitle(), renderConfirm()]
        })
      }));
    }

    return null;
  };

  const renderColumnItems = () => formattedColumns.map((item, columnIndex) => {
    var _a;

    return _jsx(Column // eslint-disable-next-line react/no-array-index-key
    , {
      optionRender: props.optionRender,
      ref: setRefs(columnIndex),
      textKey: textKey,
      readonly: props.readonly,
      className: item.className,
      itemHeight: itemHeight,
      defaultIndex: (_a = item.defaultIndex) !== null && _a !== void 0 ? _a : +props.defaultIndex,
      swipeDuration: props.swipeDuration,
      visibleItemCount: props.visibleItemCount,
      initialOptions: item[valuesKey],
      onChange: () => {
        onChange(columnIndex);
      }
    }, columnIndex);
  });

  const renderColumns = () => {
    const wrapHeight = itemHeight * props.visibleItemCount;
    const frameStyle = {
      height: `${itemHeight}px`
    };
    const columnsStyle = {
      height: `${wrapHeight}px`
    };
    const maskStyle = {
      backgroundSize: `100% ${(wrapHeight - itemHeight) / 2}px`
    };
    return _jsxs("div", Object.assign({
      ref: wrapper,
      className: clsx(bem('columns')),
      style: columnsStyle
    }, {
      children: [renderColumnItems(), _jsx("div", {
        className: clsx(bem('mask')),
        style: maskStyle
      }), _jsx("div", {
        className: clsx(BORDER_UNSET_TOP_BOTTOM, bem('frame')),
        style: frameStyle
      })]
    }));
  };

  useEffect(() => {
    if (JSON.stringify(props.columns) !== JSON.stringify(formattedColumns)) {
      resetRefs();
      format();
    } // eslint-disable-next-line react-hooks/exhaustive-deps

  }, [props.columns]);
  useEventListener('touchmove', preventDefault, {
    target: wrapper.current
  });
  useImperativeHandle(ref, () => ({
    confirm,
    getValues,
    setValues,
    getIndexes,
    setIndexes,
    getColumnIndex,
    setColumnIndex,
    getColumnValue,
    setColumnValue,
    getColumnValues,
    setColumnValues
  }));
  return _jsxs("div", Object.assign({
    className: clsx(bem(), props.className)
  }, {
    children: [props.toolbarPosition === 'top' ? renderToolbar() : null, props.loading ? _jsx(Loading, {
      className: clsx(bem('loading'))
    }) : null, props.columnsTop, renderColumns(), props.columnsBottom, props.toolbarPosition === 'bottom' ? renderToolbar() : null]
  }));
}

const Picker = forwardRef(PickerInner);
Picker.defaultProps = {
  itemHeight: 44,
  visibleItemCount: 5,
  swipeDuration: 1000,
  defaultIndex: 0,
  showToolbar: true,
  toolbarPosition: 'top'
};
export default Picker;