import * as React from 'react';
import { ForContext, getComponentRenderList } from './NodeRenderer';
import lodashSet from 'lodash.set';
import cloneDeep from 'lodash.clonedeep';
import { isScopeSlot, isSlot } from '../utils/common';
import { IBasicComponent, IComplexComponent } from '@cloudbase/cals';
import { observer } from 'mobx-react-lite';
import { ComponentNotFound } from '../utils/error';
import { InjectContext } from '../router';

interface IContext {
  virtualFields: { [sourceKey: string]: React.FunctionComponent<any> };
  codeContext: any;
  scopeContext?: { [key: string]: any };
  dataContext?: any;
  updateContext?: any;
  injectContext?: {
    SlotRender?: React.FC<
      React.PropsWithChildren<{ componentSchema: any } & Record<string, any>>
    >;
  };
  forContext?: any;
}

function checkDoNotRenderSlot(componentSchema, renderSlot, rootNode) {
  // 判断是否为 slot
  return isSlot(componentSchema) && !(renderSlot || rootNode);
}

export function getComponentChildren(
  component,
  context: {
    virtualFields;
    codeContext;
    scopeContext;
    forContext;
    injectContext;
    dataContext;
    updateContext;
  }
) {
  const { items = [] } = component;
  if (!items.length) {
    return [];
  }
  const list = items.sort(
    (a, b) => (a.extra?.xIndex || 0) - (b.extra?.xIndex || 0)
  );

  const {
    virtualFields,
    codeContext,
    scopeContext,
    forContext,
    injectContext,
    dataContext,
    updateContext,
  } = context;

  return list.map((schema) => {
    return getRenderList({
      key: schema.id,
      componentSchema: schema,
      rootNode: false,
      renderSlot: false,
      virtualFields,
      codeContext,
      scopeContext,
      forContext,
      injectContext,
      dataContext,
      updateContext,
    });
  });
}

export function generateSlotMetaMap(
  componentSchema,
  context: IContext,
  options?: {
    rootNode?: boolean;
    renderSlot: boolean;
  }
): {
  [key: string]:
    | {
        type: 'HOC';
        node: React.FunctionComponent;
      }
    | {
        type: 'ELEMENT';
        node: React.ReactElement;
      };
} {
  const { items = [] } = componentSchema;
  const {
    scopeContext,
    codeContext,
    virtualFields,
    dataContext,
    updateContext,
    injectContext,
    forContext,
  } = context;

  const slots = {};
  // eslint-disable-next-line guard-for-in
  for (const child of items) {
    // TODO 做到不依赖组件 id
    const key = child.id;
    if (isSlot(child)) {
      const isHOC = isScopeSlot(componentSchema, key);
      slots[key] = {
        type: isHOC ? 'HOC' : 'ELEMENT',
        node: isHOC
          ? (props) => {
              let clonedScopeContext = cloneDeep(scopeContext);
              lodashSet(
                clonedScopeContext,
                `${componentSchema.id}.${key}`,
                props
              );
              return (
                <InjectContext.Provider value={injectContext}>
                  <AppRender
                    key={key}
                    componentSchema={child as any}
                    renderSlot={options.renderSlot}
                    virtualFields={virtualFields}
                    codeContext={codeContext}
                    scopeContext={clonedScopeContext}
                    dataContext={dataContext}
                    updateContext={updateContext}
                  />
                </InjectContext.Provider>
              );
            }
          : () => {
              return (
                <InjectContext.Provider value={injectContext}>
                  <ForContext.Provider value={forContext}>
                    <AppRender
                      key={key}
                      componentSchema={child as any}
                      renderSlot={options?.renderSlot}
                      virtualFields={virtualFields}
                      codeContext={codeContext}
                      scopeContext={scopeContext}
                      dataContext={dataContext}
                      updateContext={updateContext}
                    />
                  </ForContext.Provider>
                </InjectContext.Provider>
              );
            },
      };
    }
  }

  return slots;
}

interface IAppRenderProps extends IContext {
  className?: string;
  componentSchema: (IBasicComponent | IComplexComponent) & {
    extra?: { emitEvents: [] };
  };
  renderSlot?;
  rootNode?: boolean;
  dataContext?: any;
  updateContext?: any;
}

function getRenderList(
  props: IAppRenderProps & {
    key?;
    forContext;
    injectContext;
  }
) {
  const {
    key,
    className,
    virtualFields,
    componentSchema,
    renderSlot,
    rootNode = true,
    codeContext,
    scopeContext = {},
    forContext = {},
    injectContext = {},
    dataContext,
    updateContext,
  } = props;

  const { items = [] } = componentSchema;
  const { genericComp = {} } = (componentSchema.extra as any) || {};

  // 判断是否为 slot
  if (checkDoNotRenderSlot(componentSchema, renderSlot, rootNode)) {
    return null;
  }

  // wrapperClass;
  // const preClassName = useRef<string>('');
  const containerEl = items[0];
  if (containerEl?.attributes && className) {
    let classNameList = (containerEl.attributes?.class || '')
      .split(/\s/)
      .filter((item) => !!item);

    // 先替换掉先前计算出来的className部分
    // if (preClassName.current) {
    //   if (preClassName.current !== className) {
    //     classNameList = classNameList.filter(
    //       (clsName) => clsName !== preClassName.current
    //     );
    //     preClassName.current = className;
    //   }
    // } else {
    //   preClassName.current = className;
    // }

    // containerEl.attributes.class = [className, ...classNameList].join(' ');
    containerEl.attributes.class = Array.from(
      new Set([className, ...classNameList])
    ).join(' ');
  }

  if (componentSchema.module && componentSchema.component) {
    const sourceKey = `${componentSchema.module}:${componentSchema.component}`;
    const Field = virtualFields[sourceKey];
    if (!Field) {
      return (
        <ComponentNotFound
          key={key}
          module={componentSchema.module}
          component={componentSchema.component}
        />
      );
    }
  }

  const context = {
    virtualFields,
    codeContext,
    scopeContext,
    forContext,
    dataContext,
    updateContext,
    injectContext,
  };

  return getComponentRenderList({
    key,
    componentSchema,
    rootNode,
    emitEvents: componentSchema.extra?.emitEvents || [],
    renderSlot,
    virtualFields,
    codeContext,
    scopeContext,
    forContext,
    dataContext,
    updateContext,
    injectContext,
    genericComp,
  });
}

export const AppRender = observer(function (props: IAppRenderProps) {
  return getRenderList({
    ...props,
    forContext: React.useContext(ForContext),
    injectContext: React.useContext(InjectContext),
  });
});
