import { Children, isValidElement, JSXElementConstructor, Key, ReactElement, ReactNode } from 'react';
import isNil from 'lodash/isNil';
import omit from 'lodash/omit';
import isFragment from 'choerodon-ui/pro/lib/_util/isFragment';
import { iteratorFindIndex, iteratorSome } from 'choerodon-ui/pro/lib/_util/iteratorUtils';
import { TabsPosition } from './enum';
import { isTabGroup, TabGroupProps } from './TabGroup';
import { TabPaneProps } from './TabPane';
import { GroupPanelMap, TabsCustomized } from './Tabs';

export function toGroups(children: ReactNode): ReactElement<TabGroupProps>[] {
  const c: ReactElement<TabGroupProps>[] = [];
  Children.forEach<ReactNode>(children, child => {
    if (child) {
      if (isFragment(child)) {
        c.push(...toGroups(child.props.children));
      } else if (isTabGroup(child)) {
        c.push(child);
      }
    }
  });
  return c;
}

export function toArray(children: ReactNode): ReactElement<TabPaneProps>[] {
  const c: ReactElement<TabPaneProps>[] = [];
  Children.forEach(children, child => {
    if (child) {
      if (isFragment(child)) {
        c.push(...toArray(child.props.children));
      } else if (isValidElement<TabPaneProps>(child)) {
        c.push(child);
      }
    }
  });
  return c;
}

export function getDefaultActiveKeyInGroup(panelMap: Map<string, TabPaneProps>): string | undefined {
  let activeKey: string | undefined;
  iteratorSome(panelMap.entries(), ([key, panel]) => {
    if (!panel.disabled) {
      activeKey = key;
      return true;
    }
    return false;
  });
  return activeKey;
}

export function getDefaultActiveKey(totalPanelsMap: Map<string, TabPaneProps>, groupedPanelsMap: Map<string, GroupPanelMap>, option: { activeKey?: string | undefined; defaultActiveKey?: string | undefined }): string | undefined {
  const { activeKey, defaultActiveKey } = option;
  if (activeKey !== undefined) {
    return activeKey;
  }
  if (defaultActiveKey !== undefined) {
    return defaultActiveKey;
  }
  const { value } = groupedPanelsMap.values().next();
  if (value) {
    const { group: { defaultActiveKey: groupDefaultActiveKey } } = value;
    if (groupDefaultActiveKey !== undefined) {
      return groupDefaultActiveKey;
    }
  }
  return getDefaultActiveKeyInGroup(totalPanelsMap);
}

export function getDefaultGroupKey(groupedPanelsMap: Map<string, GroupPanelMap>): string | undefined {
  let groupKey: string | undefined;
  iteratorSome(groupedPanelsMap.entries(), ([key, { panelsMap }]) => (
    iteratorSome(panelsMap.values(), (panel) => {
      if (!panel.disabled) {
        groupKey = key;
        return true;
      }
      return false;
    })
  ));
  return groupKey;
}

export function getActiveKeyByGroupKey(groupedPanelsMap: Map<string, GroupPanelMap>, key: string): string | undefined {
  const map = groupedPanelsMap.get(key);
  if (map) {
    const { group, panelsMap, lastActiveKey } = map;
    if (lastActiveKey) {
      return lastActiveKey;
    }
    if ('defaultActiveKey' in group) {
      return group.defaultActiveKey;
    }
    return getDefaultActiveKeyInGroup(panelsMap);
  }
}

export function generateKey(key: Key | undefined | null, index: number): string {
  return String(isNil(key) ? index : key);
}

export function getActiveIndex(map: Map<string, TabPaneProps>, activeKey: string | undefined): number {
  return activeKey === undefined ? -1 : iteratorFindIndex(map.keys(), key => key === activeKey);
}

export function setTransform(style: CSSStyleDeclaration, v = '') {
  style.transform = v;
  style.webkitTransform = v;
}

export function isTransformSupported(style: CSSStyleDeclaration): boolean {
  return 'transform' in style || 'webkitTransform' in style;
}

export function setTransition(style: CSSStyleDeclaration, v = '') {
  style.transition = v;
  style.webkitTransition = v;
}

export function getTransformPropValue(v: string) {
  return {
    transform: v,
    WebkitTransform: v,
  };
}

export function isVertical(tabBarPosition: TabsPosition | undefined): boolean {
  return tabBarPosition === TabsPosition.left || tabBarPosition === TabsPosition.right;
}

export function getTransformByIndex(index: number, tabBarPosition: TabsPosition | undefined) {
  const translate = isVertical(tabBarPosition) ? 'translateY' : 'translateX';
  return `${translate}(${-index * 100}%) translateZ(0)`;
}

export function getMarginStyle(index: number, tabBarPosition: TabsPosition | undefined) {
  const marginDirection = isVertical(tabBarPosition) ? 'marginTop' : 'marginLeft';
  return {
    [marginDirection]: `${-index * 100}%`,
  };
}

export function getStyle(el: HTMLElement, property: string): number {
  return +getComputedStyle(el).getPropertyValue(property).replace('px', '');
}

export function setPxStyle(el: HTMLElement, value: string | number, vertical: boolean) {
  value = vertical ? `0px, ${value}px, 0px` : `${value}px, 0px, 0px`;
  setTransform(el.style, `translate3d(${value})`);
}

export function getDataAttr(props: object): object {
  return Object.keys(props).reduce((prev, key) => {
    if (key === 'role' || key.startsWith('aria-') || key.startsWith('data-')) {
      prev[key] = props[key];
    }
    return prev;
  }, {});
}

function toNum(style: CSSStyleDeclaration, property: string): number {
  return +style.getPropertyValue(property).replace('px', '');
}

function getTypeValue(start: string, current: string, end: string, tabNode: HTMLElement, wrapperNode: HTMLElement): number {
  let total = getStyle(wrapperNode, `padding-${start}`);
  const { parentNode } = tabNode;
  if (parentNode) {
    [...parentNode.childNodes].some((node: HTMLElement) => {
      if (node !== tabNode) {
        // 此处对代码进行了修改 取自rc-tabs@9.4.2 这版本进行了计算方式的调整,避免了在类似modal等有动画的内容中使用的时候,计算出现错误的问题,因为在动画过程中的计算,会有一次Height width为0的情况
        // 在 9.4.2版本中 因为前几个版本的修改 refactor: rm mixin and react-create-class
        // 对dom结构进行了调整 bar不与item在一个父元素中,因此有如下代码,在c7n中暂时不进行dom结构调整
        if (node.className.includes('ink-bar')) {
          return false;
        }
        const style = getComputedStyle(node);
        total += toNum(style, `margin-${start}`);
        total += toNum(style, `margin-${end}`);
        total += toNum(style, current);

        if (style.boxSizing === 'content-box') {
          total += toNum(style, `border-${start}-width`) + toNum(style, `padding-${start}`) +
            toNum(style, `border-${end}-width`) + toNum(style, `padding-${end}`);
        }
        return false;
      }
      return true;
    });
  }

  return total;
}

export function getLeft(tabNode: HTMLElement, wrapperNode: HTMLElement): number {
  return getTypeValue('left', 'width', 'right', tabNode, wrapperNode);
}

export function getTop(tabNode: HTMLElement, wrapperNode: HTMLElement): number {
  return getTypeValue('top', 'height', 'bottom', tabNode, wrapperNode);
}

export function getHeader(props: TabPaneProps): ReactNode {
  const { tab, title } = props;
  if (typeof tab === 'function') {
    return tab(title);
  }
  if (title !== undefined) {
    return title;
  }
  if (tab !== undefined) {
    return tab;
  }
}

function sorter(item1: [string, TabPaneProps], item2: [string, TabPaneProps]) {
  const { sort = 0 } = item1[1];
  const { sort: sort2 = 0 } = item2[1];
  return sort - sort2;
}

interface normalizeOptions {
  tabDraggable?: boolean | undefined;
  tabTitleEditable?: boolean | undefined;
  tabCountHideable?: boolean | undefined;
}

export function normalizePanes(children: ReactNode, customized?: TabsCustomized | undefined | null, options?: normalizeOptions): [
  Map<string, TabPaneProps & { type: string | JSXElementConstructor<any> }>,
  Map<string, GroupPanelMap>
] {
  const groups = toGroups(children);
  const groupedPanels = new Map<string, GroupPanelMap>();
  const panelList: [string, TabPaneProps & { type: string | JSXElementConstructor<any> }][] = [];
  const panes = customized && customized.panes;
  const omitKeys: string[] = [];
  if (options) {
    const { tabDraggable, tabTitleEditable, tabCountHideable } = options;
    if (!tabDraggable) {
      omitKeys.push('sort');
    }
    if (!tabTitleEditable) {
      omitKeys.push('title');
    }
    if (!tabCountHideable) {
      omitKeys.push('showCount');
    }
  }
  const { length } = omitKeys;
  const getCustomizedPane = (key: string) => {
    if (panes) {
      const pane = panes[key];
      if (pane && !pane.hidden) {
        if (length) {
          return omit(pane, omitKeys);
        }
        return pane;
      }
    }
  };
  if (groups.length) {
    let index = 0;
    groups.forEach((group, i) => {
      if (!group.props.hidden) {
        const groupPanelList: [string, TabPaneProps & { type: string | JSXElementConstructor<any> }][] = [];
        toArray(group.props.children).forEach((child, j) => {
          if (!child.props.hidden) {
            const panelKey = generateKey(child.key, index);
            index += 1;
            groupPanelList.push([panelKey, { type: child.type, sort: j, ...child.props, ...getCustomizedPane(panelKey) }]);
          }
        });
        groupPanelList.sort(sorter);
        panelList.push(...groupPanelList);
        const groupKey = generateKey(group.key, i);
        groupedPanels.set(groupKey, {
          group: { ...group.props },
          panelsMap: new Map<string, TabPaneProps & { type: string | JSXElementConstructor<any> }>(groupPanelList),
        });
      }
    });
  } else {
    toArray(children).forEach((child, index) => {
      if (!child.props.hidden) {
        const key = generateKey(child.key, index);
        panelList.push([key, { type: child.type, sort: index, ...child.props, ...getCustomizedPane(key) }]);
      }
    });
    panelList.sort(sorter);
  }
  return [new Map<string, TabPaneProps & { type: string | JSXElementConstructor<any> }>(panelList), groupedPanels];
}

export function getTextHeight(text, font = '') {
  // 创建一个隐藏的div元素
  const container = document.createElement('div');
  container.style.visibility = 'hidden';
  container.style.position = 'absolute';
  document.body.appendChild(container);

  // 创建一个span元素来容纳文本
  const span = document.createElement('span');
  span.style.font = font;
  span.textContent = text;
  container.appendChild(span);

  // 获取字符串的高度
  const height = span.offsetHeight;

  // 移除临时元素
  document.body.removeChild(container);

  return height;
}