import { computed, reactive, ref, watch, watchEffect } from "vue";

type Item<T> = { id: string; children?: Item<T>[] } & T;

export type TreeOption = {
  label: string;
  level?: number;
  value: any;
  children?: TreeOption[];
};

export type Props = {
  modelValue: string;
  label?: string;
  hideAll?: boolean;
  allText?: string;
  notDefault?: boolean;
  disabled?: boolean;
};

export type Mapper = {
  label: string;
  level: string;
};

const treeSelectOptionCover = <T>(
  data: Item<T>[],
  mapping: Mapper
): TreeOption[] =>
  data.map(
    (item) =>
      ({
        label: (item as any)[mapping.label],
        value: item.id,
        level: (item as any)[mapping.level],
        children:
          item.children && item.children.length
            ? treeSelectOptionCover(item.children, mapping)
            : null,
      } as any)
  );

const getTreePath = (options: TreeOption[], value: string): null | string[] => {
  for (const option of options) {
    if (option.value === value) {
      return [option.value];
    } else if (option.children) {
      const cpath = getTreePath(option.children, value);
      if (cpath) {
        return [option.value, ...cpath];
      }
    }
  }
  return null;
};

const getTreeSelect = (
  options: TreeOption[],
  value: string
): null | TreeOption => {
  for (const option of options) {
    if (option.value === value) {
      return option;
    } else if (option.children) {
      const coption = getTreeSelect(option.children, value);
      if (coption) {
        return coption;
      }
    }
  }
  return null;
};

const allOption: TreeOption = { label: "全部", value: "" };

export const useTreeSelect = <T>(
  props: Props,
  request: () => Promise<Item<T>[]>,
  update: (val: string) => void,
  mapping: Mapper = { label: "name", level: "level" }
) => {
  // 原始数据
  const optionsRaw = ref<Item<T>[]>([]);
  // 级联控件需要数据
  const options = computed<TreeOption[]>(() => {
    const dataOptions = treeSelectOptionCover(optionsRaw.value, mapping);
    return props.hideAll ? dataOptions : [allOption, ...dataOptions];
  });

  // 级联控件续高亮value
  const path = computed({
    get: () => getTreePath(options.value, props.modelValue)!,
    set: (path) => {
      update(path ? path[path.length - 1] : "");
    },
  });
  const currentOption = computed(() =>
    getTreeSelect(options.value, props.modelValue)
  );

  const label = computed(
    () =>
      options.value.find((option) => option.value === props.modelValue)
        ?.label || ""
  );

  watch(
    () => props.modelValue,
    () => {
      if (!props.notDefault && !props.modelValue && options.value.length) {
        update(options.value[0].value);
      }
    },
    { immediate: true }
  );

  request().then((data) => (optionsRaw.value = data as any));

  return reactive({
    label,
    path,
    options,
    raw: optionsRaw,
    currentOption,
  });
};
