import { reactive } from 'vue';
import { hasOwn } from '@vue/shared';
import { markNodeData } from '../utils';
import { throwError } from '../../../utils/error';

// type
import type { TreeNodeData, TreeNodeOptions } from '../types/tree';
import type TreeStore from './tree-store';
import type { TreeKey, FakeNode } from '../types/tree';
import type {
  TreeNodeChildState,
  TreeNodeLoadedDefaultProps,
} from '../types/node';
import { NODE_KEY } from '../utils';

// 节点id
let nodeIdSeed = 0;

/**
 * 获取 data tree数据 中的属性
 * label 指定节点标签为节点对象的某个属性值
 * @type {string, function(data, node)}
 *
 * children	指定子树为节点对象的某个属性值
 * @type {string}
 *
 * disabled 指定节点选择框是否禁用为节点对象的某个属性值
 * @type {string, function(data, node)}
 *
 * isLeaf 指定节点是否为叶子节点，仅在指定了 lazy 属性的情况下生效
 * @type {string, function(data, node)}
 *
 * class 自定义节点类名
 *
 * @type {string, function(data, node)}
 */
const getPropsFromData = (node: Node, prop: string): any => {
  const props = node.store.props;
  const data = node.data || {};

  // 获取 tree 配置选项的属性
  const config = props[prop];

  // function
  if (typeof config === 'function') {
    return config(data, node);
  }
  // string
  else if (typeof config === 'string') {
    return data[config];
  }
  // undefined
  else if (typeof config === 'undefined') {
    const dataProp = data[prop];

    return dataProp === undefined ? '' : dataProp;
  }
};

// 获取子节点状态
const getChildState = (node: Node[]): TreeNodeChildState => {
  let all = true;
  let none = true;
  let allWithoutDisable = true;

  for (let i = 0; i < node.length; i++) {
    const nodeData = node[i];

    // 没有选中 || 是不确定状态
    if (nodeData.checked !== true || nodeData.indeterminate) {
      all = false;

      // 没有禁用
      if (!nodeData.disabled) {
        allWithoutDisable = false;
      }
    }

    // 选中 || 是不确定状态
    if (nodeData.checked !== false || nodeData.indeterminate) {
      none = false;
    }
  }

  return {
    all,
    none,
    allWithoutDisable,
    half: !all && !none,
  };
};

// 重新初始化多选
const reInitChecked = (node: Node): void => {
  // 没有子节点 || 加载中
  if (node.childNodes.length === 0 || node.loading) {
    return;
  }

  // 获取子节点状态
  const { all, none, half } = getChildState(node.childNodes);

  // 选中所有多选
  if (all) {
    node.checked = true;
    node.indeterminate = false;
  }
  // 多选不确定的状态
  else if (half) {
    node.checked = false;
    node.indeterminate = true;
  }
  // 没有选择
  else if (none) {
    node.checked = false;
    node.indeterminate = false;
  }

  const parent = node.parent;

  // 层级只有一个
  if (!parent || parent.level === 0) {
    return;
  }

  // 继续递归
  if (!node.store.checkBoxStrictly) {
    reInitChecked(parent);
  }
};

// tree 节点对象
class Node {
  // tree 节点数据对象
  data: TreeNodeData;
  // tree 存储数据对象
  store: TreeStore;
  // 当前节点id
  id: number;
  // 子节点数组
  childNodes: Node[];
  // 当前节点层级
  level: number;
  // 父节点
  parent: Node;
  // 是否显示节点
  visible: boolean;
  // 节点是否展开
  expanded: boolean;
  // 是否是当前点击的节点
  isCurrent: boolean;
  // 节点是否可以获取焦点
  canFocus: boolean;
  // 是否是叶子节点
  isLeaf: boolean;
  // 是否是叶子节点
  isLeafByUser: boolean;
  // 是否加载完成
  loaded: boolean;
  // 加载中
  loading: boolean;
  // 是否选中
  checked: boolean;
  // 多选不确定状态
  indeterminate: boolean;

  constructor(options: TreeNodeOptions) {
    // tree 节点数据对象
    this.data = null;
    // 当前节点id
    this.id = nodeIdSeed++;
    // 是否显示节点
    this.visible = true;
    // 父节点
    this.parent = null;
    // 节点是否展开
    this.expanded = false;
    // 是否是当前点击的节点
    this.isCurrent = false;
    // 节点是否可以获取焦点
    this.canFocus = false;
    // 是否选中
    this.checked = false;
    // 多选不确定状态
    this.indeterminate = false;

    // 将外部值赋值给属性
    for (const name in options) {
      // 检测是否属性是否拥有
      if (hasOwn(options, name)) {
        // 赋值给当前 class 属性
        this[name] = options[name];
      }
    }

    // 子节点
    this.childNodes = [];
    // 当前节点层级
    this.level = 0;
    // 是否加载完成
    this.loaded = false;
    // 加载中
    this.loading = false;

    // 有父节点
    if (this.parent) {
      this.level = this.parent.level + 1;
    }
  }

  // 获取key
  get key(): TreeKey {
    // tree 存储数据对象的 key 属性
    const nodeKey = this.store.key;

    // 有数据
    if (this.data) {
      // 获取 data 中对应 key 属性的值
      return this.data[nodeKey];
    }

    return null;
  }

  // 标题
  get label(): string {
    return getPropsFromData(this, 'label');
  }

  // 是否禁用
  get disabled(): boolean {
    return getPropsFromData(this, 'disabled');
  }

  // 下一个兄弟节点
  get nextSibling(): Node | null {
    const parent = this.parent;

    // 有父节点
    if (parent) {
      // 子节点是否有当前节点
      const index = parent.childNodes.indexOf(this);

      if (index) {
        return parent.childNodes[index + 1];
      }
    }

    return null;
  }

  // 上一个兄弟节点
  get prevSibling(): Node | null {
    const parent = this.parent;

    if (parent) {
      const index = parent.childNodes.indexOf(this);

      if (index > -1) {
        return index > 0 ? parent.childNodes[index - 1] : null;
      }
    }

    return null;
  }

  // 初始化数据
  initialize() {
    const store = this.store;

    // 没有 tree 存储数据对象
    if (!store) {
      throwError('ivue-tree', '[Node]store is required!');
    }

    // 注册节点
    store.registerNode(this);

    // 是否是叶子节点
    const props = store.props;
    if (props && typeof props.isLeaf !== 'undefined') {
      // 指定节点是否为叶子节点
      const isLeaf = getPropsFromData(this, 'isLeaf');

      // boolean
      if (typeof isLeaf === 'boolean') {
        this.isLeafByUser = isLeaf;
      }
    }

    // 不是懒加载子节点 && 有数据
    if (store.lazy !== true && this.data) {
      this.setData(this.data);

      // 展开全部子节点
      if (store.defaultExpandAll) {
        this.expanded = true;
        this.canFocus = true;
      }
    }
    // 是懒加载子节点 && 展开全部子节点
    else if (this.level > 0 && store.lazy && store.defaultExpandAll) {
      this.expand();
    }

    // data 不是数组
    if (!Array.isArray(this.data)) {
      markNodeData(this, this.data);
    }

    // 没有数据
    if (!this.data) {
      return;
    }

    // 默认展开的节点的 keys 数组
    const defaultExpandedKeys = store.defaultExpandedKeys;
    // 每个树节点用来作为唯一标识的属性，整棵树应该是唯一的
    const key = store.key;

    // 展开对应的节点
    if (key && defaultExpandedKeys && defaultExpandedKeys.includes(this.key)) {
      this.expand(null, store.autoExpandParent);
    }

    // 设置当前节点
    if (
      key &&
      store.currentNodeKey !== undefined &&
      // 获取key === 当前点击节点的key
      this.key === store.currentNodeKey
    ) {
      store.currentNode = this;
      store.currentNode.isCurrent = true;
    }

    // 有懒加载 初始化默认勾选节点
    if (store.lazy) {
      store.initDefaultCheckedNode(this);
    }

    // 更新叶子节点状态
    this.updateLeafState();

    // 有父节点 && 有节点层级 || 展开节点 设置可以获取焦点
    if (this.parent && (this.level === 1 || this.parent.expanded === true)) {
      this.canFocus = true;
    }
  }

  // 设置数据
  setData(data: TreeNodeData): void {
    // 不是数组
    if (!Array.isArray(data)) {
      markNodeData(this, data);
    }

    // tree 节点数据对象
    this.data = data;
    // 子节点
    this.childNodes = [];

    let children;

    // 当前节点层级为父级
    if (this.level === 0 && Array.isArray(this.data)) {
      children = this.data;
    } else {
      children = getPropsFromData(this, 'children') || [];
    }

    // 循环 tree 配置选项 的 children属性
    for (let i = 0; i < children.length; i++) {
      this.insertChild({ data: children[i] });
    }
  }

  // 插入 children 数据
  insertChild(child?: FakeNode | Node, index?: number, batch?: boolean): void {
    // 没有数据
    if (!child) {
      throwError('ivue-tree', 'insertChild error: child is required');
    }

    // 不是 tree 节点对象
    if (!(child instanceof Node)) {
      // 是否是批量插入 children
      if (!batch) {
        const children = this.getChildren(true);

        // 判断一个数组是否包含一个指定的值
        if (!children.includes(child.data)) {
          // 没有index
          if (typeof index === 'undefined' || index < 0) {
            children.push(child.data);
          }
          // 在数组中添加新数据
          else {
            children.splice(index, 0, child.data);
          }
        }
      }

      // 插入对象
      Object.assign(child, {
        // 父节点
        parent: this,
        // tree 存储数据对象
        store: this.store,
      });

      // child 设置为 reactive 深度监听数据
      child = reactive(new Node(child as TreeNodeOptions));

      if (child instanceof Node) {
        child.initialize();
      }
    }

    // 增加当前节点层级
    (child as Node).level = this.level + 1;

    // 没有下标
    if (typeof index === 'undefined' || index < 0) {
      // 子节点
      this.childNodes.push(child as Node);
    }
    // 插入子节点
    else {
      this.childNodes.splice(index, 0, child as Node);
    }
  }

  // 获取 children 属性 子树为节点对象的某个属性值
  getChildren(init = false): TreeNodeData | TreeNodeData[] {
    // 当前 children 为父级
    if (this.level === 0) {
      return this.data;
    }

    // 没有 tree 节点数据对象
    const data = this.data;
    if (!data) {
      return null;
    }

    // tree 配置选项
    const props = this.store.props;

    let children = 'children';

    // 有配置选项
    if (props) {
      // 设置是使用用户自定义的 children 属性 还是默认 children
      children = props.children || 'children';
    }

    // 没有 children 属性
    if (data[children] === undefined) {
      data[children] = null;
    }

    // 没有数据初始化 children 属性
    if (init && !data[children]) {
      data[children] = [];
    }

    return data[children];
  }

  // 收起节点
  collapse(): void {
    this.expanded = false;

    // 设置焦点为不可获取
    this.childNodes.forEach((item) => {
      item.canFocus = false;
    });
  }

  // 展开节点
  expand(callback?: () => void, expandParent?: boolean): void {
    const done = (): void => {
      // 展开父级
      if (expandParent) {
        let parent = this.parent;

        // 设置对应的父级属性
        while (parent.level > 0) {
          parent.expanded = true;

          // 指向上一级的父节点
          parent = parent.parent;
        }
      }

      // 展开节点
      this.expanded = true;

      // 有回调函数
      if (callback) {
        callback();
      }

      // 子节点数组全部都可以获取焦点
      this.childNodes.forEach((item) => {
        item.canFocus = true;
      });
    };

    if (this.shouldLoadData()) {
      this.loadData((data) => {
        if (Array.isArray(data)) {
          // 多选选中
          if (this.checked) {
            this.setChecked(true, true);
          }
          // 在显示复选框的情况下，是否严格的遵循父子不互相关联的做法
          else if (!this.store.checkBoxStrictly) {
            reInitChecked(this);
          }

          // 展开节点
          done();
        }
      });
    }

    // 展开节点
    done();
  }

  // 更新叶子节点状态
  updateLeafState(): void {
    if (
      // 是懒加载节点
      this.store.lazy === true &&
      // 没有加载完成
      this.loaded !== true &&
      // 是否是叶子节点
      typeof this.isLeafByUser !== 'undefined'
    ) {
      // 叶子节点
      this.isLeaf = this.isLeafByUser;

      return;
    }

    const childNodes = this.childNodes;

    if (
      !this.store.lazy ||
      (this.store.lazy === true && this.loaded === true)
    ) {
      // 是叶子节点
      this.isLeaf = !childNodes || childNodes.length === 0;

      return;
    }

    // 不是叶子节点
    this.isLeaf = false;
  }

  // 设置多选
  setChecked(
    value?: boolean | string,
    deep?: boolean,
    recursion?: boolean,
    passValue?: boolean
  ) {
    this.indeterminate = value === 'half';
    // 设置选中
    this.checked = value === true;

    // 在显示复选框的情况下，是否严格的遵循父子不互相关联的做法
    if (this.store.checkBoxStrictly) {
      return;
    }

    // 没有加载数据
    if (!this.shouldLoadData()) {
      // 获取子节点状态
      const { all, allWithoutDisable } = getChildState(this.childNodes);

      // 不是叶子节点 && 没有选中全部 && 全部禁用
      if (!this.isLeaf && !all && allWithoutDisable) {
        this.checked = false;
        value = false;
      }

      // 深度遍历处理所有子节点
      const handDeepChildNodes = (): void => {
        // 深度遍历
        if (deep) {
          const childNodes = this.childNodes;
          for (let i = 0; i < childNodes.length; i++) {
            const child = childNodes[i];

            // 是否有外部传入值
            passValue = passValue || value !== false;

            // 设置选中
            const isCheck = child.disabled ? child.checked : passValue;

            // 设置子节点选中状态
            child.setChecked(isCheck, deep, true, passValue);
          }

          const { half, all } = getChildState(childNodes);

          // 没有选中全部
          if (!all) {
            this.checked = all;
            this.indeterminate = half;
          }
        }
      };

      // 深度遍历处理所有子节点
      handDeepChildNodes();
    }

    const parent = this.parent;
    if (!parent || parent.level === 0) return;

    // 递归
    if (!recursion) {
      reInitChecked(parent);
    }
  }

  // 显示加载数据
  shouldLoadData(): boolean {
    return this.store.lazy === true && this.store.load && !this.loaded;
  }

  // 创建子节点
  createChildren(
    array: TreeNodeData[],
    defaultProps: TreeNodeLoadedDefaultProps = {}
  ): void {
    // 插入 children 数据
    array.forEach((item) => {
      this.insertChild(
        Object.assign({ data: item }, defaultProps),
        undefined,
        true
      );
    });
  }

  // 加载数据
  loadData(
    callback: (node: Node) => void,
    defaultProps: TreeNodeLoadedDefaultProps = {}
  ) {
    if (
      // 是懒加载节点
      this.store.lazy === true &&
      // 有加载方法
      this.store.load &&
      // 没有加载完成
      !this.loaded &&
      // 没有加载中
      (!this.loading || Object.keys(defaultProps).length)
    ) {
      // 加载中
      this.loading = true;

      // 数据加载完成的回调
      const resolve = (children: TreeNodeData[]) => {
        this.childNodes = [];

        // 创建子节点
        this.createChildren(children, defaultProps);

        // 加载完成
        this.loaded = true;
        this.loading = false;

        // 更新叶子节点状态
        this.updateLeafState();

        // callback
        if (callback) {
          callback.call(this, children);
        }
      };

      this.store.load(this, resolve);
    }
    // 普通回调
    else {
      if (callback) {
        callback.call(this);
      }
    }
  }

  // 删除子节点
  removeChild(child: Node): void {
    // 获取 children 属性 子树为节点对象的某个属性值
    const children = this.getChildren() || [];

    // 是否有匹配的数据
    const dataIndex = children.indexOf(child.data);

    // 删除子节点
    if (dataIndex > -1) {
      children.splice(dataIndex, 1);
    }

    const index = this.childNodes.indexOf(child);

    if (index > -1) {
      // 注销节点
      this.store && this.store.deregisterNode(child);

      // 清除子节点父级
      child.parent = null;

      // 删除对应的节点
      this.childNodes.splice(index, 1);
    }

    // 更新叶子节点状态
    this.updateLeafState();
  }

  // 更新子节点
  updateChildren(): void {
    // 新传入的数据
    const newData = (this.getChildren() || []) as TreeNodeData[];
    // 旧的数据
    const oldData = this.childNodes.map((node) => node.data);

    const newDataMap = {};
    const newNodes = [];

    newData.forEach((item, index) => {
      // 获取数据的key
      const key = item[NODE_KEY];

      // 节点是否存在
      const isNodeExists =
        !!key && oldData.findIndex((data) => data[NODE_KEY] === key) >= 0;
      // 更新新数据
      if (isNodeExists) {
        newDataMap[key] = { index, data: item };
      }
      // 插入新数据
      else {
        newNodes.push({ index, data: item });
      }
    });

    // 不是懒加载子节点
    if (!this.store.lazy) {
      // 删除旧数据的节点
      oldData.forEach((item) => {
        if (!newDataMap[item[NODE_KEY]]) {
          this.removeChildByData(item);
        }
      });
    }

    // 使用新的节点数据插入 children 数据
    newNodes.forEach(({ index, data }) => {
      this.insertChild({ data }, index);
    });

    // 更新叶子节点状态
    this.updateLeafState();
  }

  // 是否包含传入的节点
  contains(target: Node, deep = true): boolean {
    return (this.childNodes || []).some(
      (child) => child === target || (deep && child.contains(target))
    );
  }

  // 删除节点
  remove(): void {
    const parent = this.parent;

    if (parent) {
      parent.removeChild(this);
    }
  }

  // 插入到节点前面
  insertBefore(child: FakeNode | Node, ref: Node): void {
    let index: number;

    if (ref) {
      index = this.childNodes.indexOf(ref);
    }

    // 插入 children 数据
    this.insertChild(child, index);
  }

  // 插入到节点后面
  insertAfter(child: FakeNode | Node, ref: Node): void {
    let index: number;

    if (ref) {
      index = this.childNodes.indexOf(ref);

      if (index !== -1) {
        index += 1;
      }
    }

    // 插入 children 数据
    this.insertChild(child, index);
  }

  // 删除节点的数据
  removeChildByData(data: TreeNodeData): void {
    let targetNode: Node = null;

    this.childNodes.forEach((item) => {
      if (item.data === data) {
        targetNode = item;
      }
    });

    // 删除子节点
    if (targetNode) {
      this.removeChild(targetNode);
    }
  }
}

export default Node;
