import React, { useState, useRef } from "react";
import classNames from "classnames";
import { isChildOfType } from "../_util/is-child-of-type";
import { TabBar } from "./TabBar";
import { TabPanel } from "./TabPanel";
import { TabsProps, Tab } from "./TabProps";
import { useConfig } from "../_util/config-context";

const getHiddenPanelStyle: (
  vertical: boolean
) => React.CSSProperties = vertical => ({
  height: 0,
  width: 0,
  overflow: "hidden",
  opacity: 0,
  padding: 0,
  margin: 0,
  pointerEvents: "none",
  display: "block",
  transform: vertical ? "translate3d(10px, 0, 0)" : "translate3d(0, 10px, 0)",
});

const animatedPanelStyle: React.CSSProperties = {
  transition: "opacity .45s ease, transform .45s ease",
  transformOrigin: "center",
};

const defaultTabBarRender = c => <a>{c}</a>;

export const Tabs = React.forwardRef(function Tabs(
  {
    tabs = [],
    destroyInactiveTabPanel = true,
    activeId,
    onActive,
    defaultActiveId,
    children,
    animated = true,
    placement,
    ceiling,
    addon,
    maxHeight,
    className,
    tabBarRender = defaultTabBarRender,
    tabBarStyle,
    activeTabAutoScrollIntoView = true,
    disableTabScrolling = false,
    ...props
  }: TabsProps,
  ref: React.Ref<HTMLDivElement>
) {
  const { classPrefix } = useConfig();
  // 如果都没有定义，获取第一个没被禁用的选项卡 id
  if (
    typeof activeId === "undefined" &&
    typeof defaultActiveId === "undefined"
  ) {
    const firstAvailable = tabs.find(x => !x.disabled);
    // eslint-disable-next-line no-param-reassign
    defaultActiveId = firstAvailable ? firstAvailable.id : null;
  }

  // 注意 useState 是个 Hooks，不能写在 if 里面
  const [internalActiveId, setInternalActiveId] = useState(defaultActiveId);

  // 非受控模式下，使用内部状态
  if (typeof activeId === "undefined") {
    // eslint-disable-next-line no-param-reassign
    activeId = internalActiveId;
    // eslint-disable-next-line no-param-reassign
    onActive = (onActive => (tab: Tab, evt: React.SyntheticEvent) => {
      setInternalActiveId(tab.id);
      if (typeof onActive === "function") {
        onActive(tab, evt);
      }
    })(onActive);
  }

  const panelRenderedSetRef = useRef<Set<string>>(new Set());

  function getTabPanels() {
    const panelRenderedSet = panelRenderedSetRef.current;
    const panelChildren = [];
    React.Children.forEach(children, (child, index) => {
      if (isChildOfType(child, TabPanel)) {
        const { props, key } = child;
        const { style = {}, id } = props;

        const active = activeId === props.id;

        if (active && !panelRenderedSet.has(activeId)) {
          panelRenderedSet.add(activeId);
        }

        if (!active) {
          Object.assign(style, getHiddenPanelStyle(placement === "left"));
        }

        if (animated) {
          Object.assign(style, animatedPanelStyle);
        }

        if (
          child.props.forceRender ||
          (destroyInactiveTabPanel && active) ||
          (!destroyInactiveTabPanel && panelRenderedSet.has(props.id))
        ) {
          panelChildren.push(
            React.cloneElement(child, { key: key || id, style })
          );
        } else {
          panelChildren.push(
            <TabPanel id={id} key={key || id} style={style} />
          );
        }
      } else if (React.isValidElement(child)) {
        panelChildren.push(React.cloneElement(child, { key: index }));
      } else {
        panelChildren.push(<div key={index}>{child}</div>);
      }
    });
    return panelChildren;
  }

  const containerClassName = classNames(`${classPrefix}-tabs`, {
    // 垂直布局
    [`${classPrefix}-tabs--vertical`]: placement === "left",
    [`${classPrefix}-tabs--ceiling`]: ceiling,
    [className]: className,
  });

  return (
    <div ref={ref} className={containerClassName} {...props}>
      <TabBar
        tabs={tabs}
        activeId={activeId}
        onActive={onActive}
        disableTabScrolling={disableTabScrolling}
        addon={addon}
        vertical={placement === "left"}
        maxHeight={maxHeight}
        tabBarRender={tabBarRender}
        style={tabBarStyle}
        activeTabAutoScrollIntoView={activeTabAutoScrollIntoView}
      />
      {getTabPanels()}
    </div>
  );
});

Tabs.displayName = "Tabs";
