import React, { useState, useContext, useEffect, useMemo, useCallback } from 'react';
import { Tabs } from 'antd';
import { context, provider as TabsProvider } from './context';
import { UmiComponentProps, CONTEXT_ACTIONS, Tab, Position, ContextMenuLabels } from './types';
import { isTabActive, getTabKeyFromLocation } from './utils';
import ContextMenu from './ContextMenu';
import styles from './index.less';

const { TabPane } = Tabs;

/**
 * TabBar component placed on top of a page
 */
const TabBar: React.FC<{
  location: any;
  history: any;
  defaultChildren: React.ReactNode;
  contextMenuLabels?: ContextMenuLabels
}> = props => {
  const [targetTab, setTargetTab] = useState<Tab>();
  const [position, setPosition] = useState<Position>();
  const store = useContext(context);
  const { tabs, dispatch } = store;

  const { location, defaultChildren, history, contextMenuLabels } = props;


  const isLocationInTab = useMemo(() => {
    return tabs.some(
      tab => getTabKeyFromLocation(tab.location) === getTabKeyFromLocation(location)
    );
  }, [location]);


  const handleTabChange = useCallback((key)=>{
    const tab = tabs.find((t) => getTabKeyFromLocation(t.location)  === key);
    if (tab && !isTabActive(key, location)) {
      const {query, pathname, hash} = tab.location;
      history.push({pathname, query, hash});
    }
  }, [tabs, location, history])
  /**
   * Handle tab remove
   * @param tabKey Key of tab to be removed
   * @param action Name of action
   */
  const handleEdit = (tabKey: any, action: 'add' | 'remove') => {
    if (action === 'remove') {
      const tabIndex = tabs.findIndex(tab => getTabKeyFromLocation(tab.location) === tabKey);
      if (tabIndex < 0) return;
      let nextActiveTab;
      if (isTabActive(tabKey, location)) {
        nextActiveTab = tabs[tabIndex + 1] ||
          tabs[tabIndex - 1] || { location: {pathname: '/'} };
      }
      if (nextActiveTab) {
        const {query, pathname, hash} = nextActiveTab.location;
        history.push({pathname, query, hash});
      }
      const newTabs = [...tabs];
      newTabs.splice(tabIndex, 1);
      dispatch({
        type: CONTEXT_ACTIONS.UPDATE_TABS,
        payload: newTabs,
      });
    }
  };

  /**
   * Show context menu when right click tab menus
   */
  const handleContextMenu = (e: React.MouseEvent, tab: Tab) => {
    e.preventDefault();
    setTargetTab(tab);
    setPosition({ x: e.clientX, y: e.clientY });
  }

  const attachEvents = () => {
    function cleanTargetTab() {
      setTargetTab(undefined)
    }
    document.addEventListener('click', cleanTargetTab);
    return () => {
      document.removeEventListener('click', cleanTargetTab);
    }
  }

  useEffect(attachEvents, []);

  return (
    <div className='ant-page-tabs'>
      <Tabs
        className='ant-page-tab-list'
        hideAdd
        type="editable-card"
        onChange={handleTabChange}
        onEdit={handleEdit}
        activeKey={getTabKeyFromLocation(location)}
      >
        {tabs.map(tab => {
          return (
            <TabPane
              tab={
                <span
                  onContextMenu={(e) => { handleContextMenu(e, tab) }}
                  className={styles.tabLabel}
                >
                  {tab.route.tabLocalName || tab.route.name}
                </span>
              }
              key={getTabKeyFromLocation(tab.location)}>
              {tab.children}
            </TabPane>
          );
        })}
      </Tabs>
      {!isLocationInTab && defaultChildren}
      <ContextMenu
        activeKey={getTabKeyFromLocation(location)}
        tab={targetTab}
        position={position}
        history={history}
        handleTabClose={handleEdit}
        menuLabels={contextMenuLabels}
      />
    </div>
  );
};

interface TabLayoutProps extends UmiComponentProps {
  contextMenuLabels?: ContextMenuLabels
}

const TabLayout: React.FC<TabLayoutProps> = props => {
  const { children, location, history, contextMenuLabels } = props;

  return (
    <TabsProvider>
      <TabBar
        history={history}
        location={location}
        defaultChildren={children}
        contextMenuLabels={contextMenuLabels}
      />
    </TabsProvider>
  );
};

export default TabLayout;