<script>
import XbCheckbox from 'xinbao-ui/packages/checkbox';
import XbRadio from 'xinbao-ui/packages/radio';
import { isEqual } from 'xinbao-ui/src/utils/util';

const stopPropagation = e => e.stopPropagation();

export default {
  inject: ['panel'],

  components: {
    XbCheckbox,
    XbRadio
  },

  props: {
    node: {
      required: true
    },
    nodeId: String
  },

  computed: {
    config() {
      return this.panel.config;
    },
    isLeaf() {
      return this.node.isLeaf;
    },
    isDisabled() {
      return this.node.isDisabled;
    },
    checkedValue() {
      return this.panel.checkedValue;
    },
    isChecked() {
      return this.node.isSameNode(this.checkedValue);
    },
    inActivePath() {
      return this.isInPath(this.panel.activePath);
    },
    inCheckedPath() {
      if (!this.config.checkStrictly) return false;

      return this.panel.checkedNodePaths
        .some(checkedPath => this.isInPath(checkedPath));
    },
    value() {
      return this.node.getValueByOption();
    }
  },

  methods: {
    handleExpand() {
      const { panel, node, isDisabled, config } = this;
      const { multiple, checkStrictly } = config;

      if (!checkStrictly && isDisabled || node.loading) return;

      if (config.lazy && !node.loaded) {
        panel.lazyLoad(node, () => {
          // do not use cached leaf value here, invoke this.isLeaf to get new value.
          const { isLeaf } = this;

          if (!isLeaf) this.handleExpand();
          if (multiple) {
            // if leaf sync checked state, else clear checked state
            const checked = isLeaf ? node.checked : false;
            this.handleMultiCheckChange(checked);
          }
        });
      } else {
        panel.handleExpand(node);
      }
    },

    handleCheckChange() {
      const { panel, value, node } = this;
      panel.handleCheckChange(value);
      panel.handleExpand(node);
    },

    handleMultiCheckChange(checked) {
      this.node.doCheck(checked);
      this.panel.calculateMultiCheckedValue();
    },

    isInPath(pathNodes) {
      const { node } = this;
      const selectedPathNode = pathNodes[node.level - 1] || {};
      return selectedPathNode.uid === node.uid;
    },

    renderPrefix(h) {
      const { isLeaf, isChecked, config } = this;
      const { checkStrictly, multiple } = config;

      if (multiple) {
        return this.renderCheckbox(h);
      } else if (checkStrictly) {
        return this.renderRadio(h);
      } else if (isLeaf && isChecked) {
        return this.renderCheckIcon(h);
      }

      return null;
    },

    renderPostfix(h) {
      const { node, isLeaf } = this;

      if (node.loading) {
        return this.renderLoadingIcon(h);
      } else if (!isLeaf) {
        return this.renderExpandIcon(h);
      }

      return null;
    },

    renderCheckbox(h) {
      const { node, config, isDisabled } = this;
      const events = {
        on: { change: this.handleMultiCheckChange },
        nativeOn: {}
      };

      if (config.checkStrictly) { // when every node is selectable, click event should not trigger expand event.
        events.nativeOn.click = stopPropagation;
      }

      return (
        <xb-checkbox
          value={node.checked}
          indeterminate={node.indeterminate}
          disabled={isDisabled}
          {...events}
        ></xb-checkbox>
      );
    },

    renderRadio(h) {
      let { checkedValue, value, isDisabled } = this;

      // to keep same reference if value cause radio's checked state is calculated by reference comparision;
      if (isEqual(value, checkedValue)) {
        value = checkedValue;
      }

      return (
        <xb-radio
          value={checkedValue}
          label={value}
          disabled={isDisabled}
          onChange={this.handleCheckChange}
          nativeOnClick={stopPropagation}>
          {/* add an empty element to avoid render label */}
          <span></span>
        </xb-radio>
      );
    },

    renderCheckIcon(h) {
      // return (
      //   <i class="xb_icon_correct_linear xb-cascader-node__prefix"></i>
      // );
    },

    renderLoadingIcon(h) {
      return (
        <i class="xb_icon_loading xb-cascader-node__postfix"></i>
      );
    },

    renderExpandIcon(h) {
      return (
        <i class="xb_icon_right xb-cascader-node__postfix"></i>
      );
    },

    renderContent(h) {
      const { panel, node } = this;
      const render = panel.renderLabelFn;
      const vnode = render
        ? render({ node, data: node.data })
        : null;

      return (
        <span class="xb-cascader-node__label">{vnode || node.label}</span>
      );
    }
  },

  render(h) {
    const {
      inActivePath,
      inCheckedPath,
      isChecked,
      isLeaf,
      isDisabled,
      config,
      nodeId
    } = this;
    const { expandTrigger, checkStrictly, multiple } = config;
    const disabled = !checkStrictly && isDisabled;
    const events = { on: {} };

    if (expandTrigger === 'click') {
      events.on.click = this.handleExpand;
    } else {
      events.on.mouseenter = e => {
        this.handleExpand();
        this.$emit('expand', e);
      };
      events.on.focus = e => {
        this.handleExpand();
        this.$emit('expand', e);
      };
    }
    if (isLeaf && !isDisabled && !checkStrictly && !multiple) {
      events.on.click = this.handleCheckChange;
    }

    return (
      <li
        role="menuitem"
        id={nodeId}
        aria-expanded={inActivePath}
        tabindex={disabled ? null : -1}
        class={{
          'xb-cascader-node': true,
          'is-selectable': checkStrictly,
          'in-active-path': inActivePath,
          'in-checked-path': inCheckedPath,
          'is-active': isChecked,
          'is-disabled': disabled
        }}
        {...events}>
        {this.renderPrefix(h)}
        {this.renderContent(h)}
        {this.renderPostfix(h)}
      </li>
    );
  }
};
</script>
