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

import React, { useContext, useEffect, useState } from 'react';
import cls from 'clsx';
import { Cross, Success } from '@react-vant/icons';
import { extend } from '../utils';
import { useSetState, useUpdateEffect } from '../hooks';
import Tabs from '../tabs';
import ConfigProviderContext from '../config-provider/ConfigProviderContext';
const INITIAL_STATE = {
  tabs: [],
  activeTab: 0
};

const Cascader = props => {
  const {
    prefixCls,
    createNamespace,
    locale
  } = useContext(ConfigProviderContext);
  const [bem] = createNamespace('cascader', prefixCls);
  const [internalValue, updateInternalValue] = useState(undefined);
  const [state, updateState] = useSetState(INITIAL_STATE);
  const {
    text: textKey,
    value: valueKey,
    children: childrenKey
  } = extend({
    text: 'text',
    value: 'value',
    children: 'children'
  }, props.fieldNames);

  const getSelectedOptionsByValue = (options, value) => {
    for (let i = 0; i < options.length; i++) {
      const option = options[i];

      if (option[valueKey] === value) {
        return [option];
      }

      if (option[childrenKey]) {
        const selectedOptions = getSelectedOptionsByValue(option[childrenKey], value);

        if (selectedOptions) {
          return [option, ...selectedOptions];
        }
      }
    }

    return undefined;
  };

  const updateTabs = () => {
    if (internalValue || internalValue === 0) {
      const selectedOptions = getSelectedOptionsByValue(props.options, internalValue);

      if (selectedOptions) {
        let optionsCursor = props.options;
        const tabs = selectedOptions.map(option => {
          const tab = {
            options: optionsCursor,
            selectedOption: option
          };
          const next = optionsCursor.find(item => item[valueKey] === option[valueKey]);

          if (next) {
            optionsCursor = next[childrenKey];
          }

          return tab;
        });

        if (optionsCursor) {
          tabs.push({
            options: optionsCursor,
            selectedOption: null
          });
        }

        updateState({
          tabs
        });
        setTimeout(() => {
          updateState({
            activeTab: tabs.length - 1
          });
        }, 0);
        return;
      }
    }

    updateState({
      tabs: [{
        options: props.options,
        selectedOption: null
      }]
    });
  };

  const onSelect = (option, tabIndex) => {
    var _a, _b;

    if (option.disabled) {
      return;
    }

    let tabs = JSON.parse(JSON.stringify(state.tabs));
    tabs[tabIndex].selectedOption = option;

    if (tabs.length > tabIndex + 1) {
      tabs = tabs.slice(0, tabIndex + 1);
    }

    if (option[childrenKey]) {
      const nextTab = {
        options: option[childrenKey],
        selectedOption: null
      };

      if (tabs[tabIndex + 1]) {
        tabs[tabIndex + 1] = nextTab;
      } else {
        tabs.push(nextTab);
      }

      updateState({
        tabs,
        activeTab: state.activeTab + 1
      });
    }

    const selectedOptions = tabs.map(tab => tab.selectedOption).filter(Boolean);
    const eventParams = {
      value: option[valueKey],
      tabIndex,
      selectedOptions
    };
    updateInternalValue(option[valueKey]);
    (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, eventParams);

    if (!option[childrenKey]) {
      (_b = props.onFinish) === null || _b === void 0 ? void 0 : _b.call(props, eventParams);
    }
  };

  const onClose = () => {
    var _a;

    return (_a = props.onClose) === null || _a === void 0 ? void 0 : _a.call(props);
  };

  const onClickTab = ({
    name,
    title
  }) => {
    var _a;

    updateState({
      activeTab: +name
    });
    (_a = props.onClickTab) === null || _a === void 0 ? void 0 : _a.call(props, +name, title);
  };

  const getStateFromValue = value => {
    if (!value || !value.length) return INITIAL_STATE;

    try {
      const initialState = {
        activeTab: value.length - 1,
        tabs: []
      };
      value.reduce((options, v) => {
        const selectedOption = options.find(tabs => tabs[valueKey] === v);
        if (!selectedOption) throw Error('Cascader: unable to match options correctly, Please check value or defaultValue props.');
        initialState.tabs.push({
          options,
          selectedOption
        });
        return selectedOption[childrenKey];
      }, props.options);
      return initialState;
    } catch (error) {
      // eslint-disable-next-line no-console
      console.error(error);
      return INITIAL_STATE;
    }
  };

  const renderCloseIcon = () => {
    if (!props.closeable) return null;

    if (props.closeIcon) {
      return React.cloneElement(props.closeIcon, {
        className: cls(bem('close-icon')),
        onClick: onClose
      });
    }

    return _jsx(Cross, {
      className: cls(bem('close-icon')),
      onClick: onClose
    });
  };

  const renderHeader = () => _jsxs("div", Object.assign({
    className: cls(bem('header'))
  }, {
    children: [_jsx("h2", Object.assign({
      className: cls(bem('title'))
    }, {
      children: props.title
    })), renderCloseIcon()]
  }));

  const renderOption = (option, selectedOption, tabIndex) => {
    const selected = selectedOption && option[valueKey] === selectedOption[valueKey];
    const color = option.color || (selected ? props.activeColor : undefined);
    const Text = props.optionRender ? props.optionRender({
      option,
      selected
    }) : _jsx("span", {
      children: option[textKey]
    });
    return _jsxs("li", Object.assign({
      className: cls(bem('option', {
        selected,
        disabled: option.disabled
      }), option.className),
      style: {
        color
      },
      onClick: () => onSelect(option, tabIndex)
    }, {
      children: [Text, selected ? _jsx(Success, {
        className: cls(bem('selected-icon'))
      }) : null]
    }), option[valueKey]);
  };

  const renderOptions = (options, selectedOption, tabIndex) => _jsx("ul", Object.assign({
    className: cls(bem('options'))
  }, {
    children: options.map(option => renderOption(option, selectedOption, tabIndex))
  }), tabIndex);

  const renderTab = (tab, tabIndex) => {
    const {
      options,
      selectedOption
    } = tab;
    const title = selectedOption ? selectedOption[textKey] : props.placeholder || locale.vanCascader.select;
    return _jsx(Tabs.TabPane, Object.assign({
      title: title,
      titleClass: cls(bem('tab', {
        unselected: !selectedOption
      }))
    }, {
      children: renderOptions(options, selectedOption, tabIndex)
    }), tabIndex);
  };

  const renderTabs = () => _jsx(Tabs, Object.assign({
    animated: true,
    active: state.activeTab,
    className: cls(bem('tabs')),
    color: props.activeColor,
    swipeThreshold: 0,
    swipeable: props.swipeable,
    onClickTab: onClickTab
  }, {
    children: state.tabs.map(renderTab)
  }));

  useEffect(() => {
    updateTabs();
  }, [JSON.stringify(props.options)]);
  useEffect(() => {
    var _a;

    const value = (_a = props.value) !== null && _a !== void 0 ? _a : props.defaultValue;
    if (!value || Array.isArray(value) && !value.length) return;
    const initialState = getStateFromValue(value);
    updateState(initialState);
  }, []);
  useUpdateEffect(() => {
    const initialState = getStateFromValue(props.value);
    updateState(initialState);
  }, [props.value]);
  useUpdateEffect(() => {
    if (internalValue || internalValue === 0) {
      const values = state.tabs.map(tab => {
        var _a;

        return (_a = tab.selectedOption) === null || _a === void 0 ? void 0 : _a[valueKey];
      });

      if (values.includes(internalValue)) {
        return;
      }
    }

    updateTabs();
  }, [internalValue]);
  return _jsxs("div", Object.assign({
    className: cls(bem())
  }, {
    children: [renderHeader(), state.tabs.length ? renderTabs() : null]
  }));
};

Cascader.defaultProps = {
  closeable: true,
  swipeable: true,
  options: []
};
export default Cascader;