import React, { useEffect, useMemo, useRef, useState } from "react";
import { List } from "../../list";
import { EmptyTip } from "../../tips";
import { noop } from "../../_util/noop";
import { useIsomorphicLayoutEffect } from "../../_util/use-isomorphic-layout-effect";
import { getOptions } from "../CascaderBox";
import { CascaderLoading } from "../CascaderLoading";
import { CascaderBoxProps, CascaderData } from "../CascaderProps";

export function CascaderMenuLists({
  data,
  curValue,
  onLoad,
  onClose,
  changeOnSelect,
  onSelect,
  expandTrigger,
  scheduleUpdate,
  tips,
  bottomTips,
  onScrollBottom = noop,
}: CascaderBoxProps) {
  const enterTimerRef = useRef(null);
  // 面板可用值（动态加载时预先传输 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 lists = useMemo(() => {
    const lists = [data];
    for (let i = 0; i < internalValue.length; ++i) {
      const option = lists[i]?.options?.find(
        option => option.value === internalValue[i]
      );
      if (option && option.child) {
        lists.push(option.child as CascaderData);
      } else {
        break;
      }
    }
    return lists;
  }, [data, internalValue]);

  useEffect(() => {
    if (scheduleUpdate) {
      scheduleUpdate();
    }
  }, [lists, 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) {
      onClose();
    }
  }

  return (
    <>
      {lists.map(({ options }, index) => (
        <List
          type="option"
          key={["initial", ...internalValue].slice(0, index + 1).join("$")}
          onScrollBottom={() => onScrollBottom(internalValue.slice(0, index))}
        >
          {options ? (
            <>
              {tips && (
                <List.StatusTip>
                  {tips(internalValue.slice(0, index))}
                </List.StatusTip>
              )}
              {options.map(({ value, label, child, disabled }) => {
                const selected = internalValue[index] === value;

                const triggerProps =
                  expandTrigger === "hover" && child
                    ? {
                        onMouseEnter: (event: React.MouseEvent) => {
                          enterTimerRef.current = setTimeout(() => {
                            handleSelect(index, value, {
                              event,
                              hasChild: true,
                            });
                          }, 150);
                        },
                        onMouseLeave: () => {
                          clearTimeout(enterTimerRef.current);
                        },
                      }
                    : {
                        onClick: (event: React.MouseEvent) => {
                          handleSelect(index, value, {
                            event,
                            hasChild: Boolean(child),
                          });
                        },
                      };

                const itemProps = {
                  ...triggerProps,
                  key: [...internalValue.slice(0, index), value].join("/"),
                  disabled,
                  selected,
                };

                if (child) {
                  return <List.SubMenu {...itemProps} label={label || value} />;
                }

                return <List.Item {...itemProps}>{label || value}</List.Item>;
              })}
              {options.length === 0 && (
                <List.StatusTip>
                  <EmptyTip />
                </List.StatusTip>
              )}
              {bottomTips && (
                <List.StatusTip>
                  {bottomTips(internalValue.slice(0, index))}
                </List.StatusTip>
              )}
            </>
          ) : (
            <List.StatusTip>
              <CascaderLoading
                onLoad={() =>
                  onLoad(internalValue, getOptions(data, internalValue))
                }
              />
            </List.StatusTip>
          )}
        </List>
      ))}
    </>
  );
}

CascaderMenuLists.displayName = "CascaderMenuLists";
