import React, { useMemo, useState } from "react";
import classNames from "classnames";
import { DropdownBox } from "../dropdown/Dropdown";
import {
  CascaderBoxProps,
  CascaderData,
  CascaderOption,
} from "./CascaderProps";
import { Tabs, TabPanel } from "../tabs";
import { CascaderLoading } from "./CascaderLoading";
import { EmptyTip } from "../tips";
import { useIsomorphicLayoutEffect } from "../_util/use-isomorphic-layout-effect";
import { useConfig } from "../util";

export function getOptions(
  data: CascaderData,
  valueList: CascaderBoxProps["curValue"]
): CascaderOption[] {
  const options = [];

  let curLevel = data;
  valueList.forEach(value => {
    const option: CascaderOption = curLevel?.options?.find(
      option => option.value === value
    ) || { value, label: null };
    options.push(option);
    if (option && option.child) {
      curLevel = option.child as CascaderData;
    } else {
      curLevel = {} as any;
    }
  });

  return options.filter(Boolean);
}

export function CascaderBox({
  data,
  curValue,
  onLoad,
  onClose,
  changeOnSelect,
  scheduleUpdate,
  multiple,
  isSelected,
  onSelect,
  onDeselect,
  className,
  style,
}: CascaderBoxProps) {
  const { classPrefix } = useConfig();

  // 面板可用值（动态加载时预先传输 value 场景，此时值无法和面板数据对应）
  const availableValue = useMemo(() => {
    const lists = [data];
    const value: string[] = [];
    for (let i = 0; i < curValue?.length; ++i) {
      const option = lists[i]?.options?.find(
        option => option.value === curValue[i]
      );
      if (option) {
        value.push(curValue[i]);
      }
      if (option && option.child) {
        lists.push(option.child as CascaderData);
      } else {
        break;
      }
    }
    return value;
  }, [curValue, data]);

  // 内部状态
  const [internalValue, setInternalValue] = useState(availableValue || []);
  useIsomorphicLayoutEffect(() => {
    setInternalValue(availableValue || []);
  }, [curValue]);

  // 面板
  const panels = useMemo(() => {
    const panels = [data];
    for (let i = 0; i < internalValue.length; ++i) {
      const option = panels[i]?.options?.find(
        option => option.value === internalValue[i]
      );
      if (option && option.child) {
        panels.push(option.child as CascaderData);
      } else {
        break;
      }
    }
    return panels;
  }, [data, internalValue]);
  const [curPanelIndex, setCurPanelIndex] = useState(panels.length - 1);

  useIsomorphicLayoutEffect(() => {
    if (scheduleUpdate) {
      scheduleUpdate();
    }
  }, [curPanelIndex, scheduleUpdate]);

  function handleSelect(index, value, { event, hasChild }) {
    const newValue = [...internalValue.slice(0, index), value];
    if (changeOnSelect) {
      onSelect(newValue, { event, options: getOptions(data, newValue) });
    } else {
      setInternalValue(newValue);
      if (!hasChild) {
        onSelect(newValue, { event, options: getOptions(data, newValue) });
      }
    }

    if (hasChild) {
      setCurPanelIndex(index + 1);
    } else if (!multiple) {
      onClose();
    }
  }

  return (
    <DropdownBox className={className} style={style}>
      <div
        className={classNames(
          `${classPrefix}-cascader-menu`,
          `${classPrefix}-cascader-menu__col${(panels[curPanelIndex] &&
            panels[curPanelIndex].col) ||
            4}`
        )}
      >
        <Tabs
          className={`${classPrefix}-tabs--bordered`}
          animated={false}
          activeId={String(curPanelIndex)}
          onActive={({ id }) => setCurPanelIndex(+id)}
          tabs={panels.map(({ title }, index) => ({
            id: String(index),
            label: title,
          }))}
        >
          {panels.map(({ options }, index) => {
            return (
              <TabPanel id={String(index)} key={index}>
                {options ? (
                  <ul
                    className={`${classPrefix}-cascader-menu__list`}
                    style={
                      options.length === 0
                        ? { margin: 0, overflow: "hidden" }
                        : {}
                    }
                  >
                    {options.map(({ value, label, child, disabled }) => {
                      const optionValue = [
                        ...internalValue.slice(0, index),
                        value,
                      ];
                      const selected = multiple
                        ? isSelected(optionValue)
                        : internalValue[index] === value;
                      return (
                        <li
                          key={[...internalValue.slice(0, index), value].join(
                            "/"
                          )}
                        >
                          <div
                            className={classNames(
                              `${classPrefix}-cascader-menu__label`,
                              {
                                "is-selected": selected,
                                "is-disabled": disabled,
                              }
                            )}
                            onClick={event => {
                              if (disabled) {
                                return;
                              }
                              if (multiple && selected) {
                                onDeselect(optionValue, {
                                  event,
                                  options: getOptions(data, optionValue),
                                });
                                return;
                              }
                              handleSelect(index, value, {
                                event,
                                hasChild: Boolean(child),
                              });
                            }}
                          >
                            {label || value}
                          </div>
                        </li>
                      );
                    })}
                    {options.length === 0 && <EmptyTip />}
                  </ul>
                ) : (
                  <ul
                    className={`${classPrefix}-cascader-menu__list`}
                    style={{ margin: 0, overflow: "hidden" }}
                  >
                    <CascaderLoading
                      onLoad={() =>
                        onLoad(internalValue, getOptions(data, internalValue))
                      }
                    />
                  </ul>
                )}
              </TabPanel>
            );
          })}
        </Tabs>
      </div>
    </DropdownBox>
  );
}

CascaderBox.displayName = "CascaderBox";
