import React, { PureComponent } from 'react';
import isEqual from 'lodash/isEqual';
import ComErrorBoundary from './ComErrorBoundary';
import { define, modules, moduleDependencies, dependencyModules } from 'utils/define';
import { getActions } from 'utils/interaction';
import defaultActions from 'constants/default-actions';
import { DataFrom } from 'models/component.types';
import {
  dispatchDebugScreenEvent,
  getDebugConfigFromSessionStorage,
  isCloseAutoUpdateOnEasyDebugPage,
} from '@/utils/debug-screen';

interface IProps {
  id: number;
  data: any;
  dataFrom: DataFrom;
  autoUpdate: {
    isAuto: boolean;
    interval: number;
  } | null;
  base: {
    name: string;
    version: string;
    module_name: string;
  };
  width: number;
  height: number;
  top: number;
  left: number;
  uniqueTag: string;
  hideDefault: boolean;
  configuration: any[];
  childrenConfig: {
    id: number;
    events: any[];
    dataFrom: DataFrom;
    autoUpdate: {
      isAuto: boolean;
      interval: number;
    } | null;
  }[];
  childrenData: {
    id: number;
    data: any;
    dataType: string;
  }[];
  dataType: string;
  childrenEvents: any[];
  tenantId?: number;
  events: any[];
  bindedInteractionState: any;
  dataCallbackValues: { [x: string]: any };
  interactionCallbackValues: { [x: string]: any };
  childrenDataCallbackValues: any[];
  iState: any;
  iActiveState: any;
  actions: any[];
  name: string;
  getComponentData?: (id: number, controller: AbortController) => void;
  onRelative?: () => void;
  emitEvent: (param) => void;
}

interface IState {
  loadedScript: boolean;
  component: any;
}

const MAX_DELAY = 2147483;

export default class EasyVComponent extends PureComponent<IProps, IState> {
  isComponentMounted = true;
  random = 0;
  controller: AbortController;
  interval: number;
  constructor(props) {
    super(props);
    this.controller = window.AbortController && new AbortController();
    this.state = {
      loadedScript: false,
      component: null,
    };
  }

  componentDidMount() {
    const {
      id,
      base,
      dataFrom,
      childrenConfig = [],
      childrenData = [],
      dataType,
      childrenEvents = [],
      tenantId,
    } = this.props;
    const { version, module_name } = base;

    if (version && module_name) {
      // 加载组件js
      this.random = Math.random();
      define(
        `com_${id}_${this.random}`,
        [module_name + '@' + version],
        (com) => {
          !window.component && (window.component = {});
          window.component[module_name + '@' + version] = com;
          if (this.isComponentMounted) {
            this.setState((prev) => ({
              ...prev,
              loadedScript: true,
              component: com,
            }));
          }
        },
        tenantId,
      );

      this.setIntervalUpdate();
      this.emit(
        this.props.events.filter((d) => d.trigger === 'dataChange'),
        this.props.data,
      );

      childrenEvents.map((d) => {
        const childData = childrenData.find((o) => o.id === d.id);
        const childEvents = d.events.filter((d) => d.trigger === 'dataChange');
        if (childEvents.length) {
          this.emit(childEvents, childData && childData.data);
        }
      });
    }

    // 初始化组件数据
    // TODO增加url标识不请求数据？
    if (dataFrom === DataFrom.DEFAULT && dataType !== 'static') {
      this.props.getComponentData && this.props.getComponentData(id, this.controller);
    }

    childrenConfig.forEach((d, i) => {
      if (
        childrenConfig[i]?.dataFrom === DataFrom.DEFAULT &&
        childrenData[i] &&
        childrenData[i].dataType !== 'static'
      ) {
        this.props.getComponentData && this.props.getComponentData(d.id, this.controller);
      }
    });
  }

  setIntervalUpdate = () => {
    const { autoUpdate, id, childrenConfig = [], dataFrom } = this.props;
    const { isAuto, interval } = autoUpdate || {};
    if (dataFrom === DataFrom.DEFAULT && isAuto && interval) {
      // if (isCloseAutoUpdateOnEasyDebugPage()) return;
      window.clearInterval(this.interval);
      this.interval = window.setInterval(() => {
        if (isCloseAutoUpdateOnEasyDebugPage()) {
          return;
        }
        this.props.getComponentData && this.props.getComponentData(id, this.controller);
      }, (interval >= MAX_DELAY ? MAX_DELAY : interval) * 1000);
    }

    // 子组件的自动更新
    childrenConfig.map((d) => {
      const { dataFrom: childDataFrom, autoUpdate: childAutoUpdate, id } = d;
      const { isAuto: childIsAuto, interval: childInterval } = childAutoUpdate || {};
      if (childDataFrom === DataFrom.DEFAULT && childIsAuto && childInterval) {
        // if (isCloseAutoUpdateOnEasyDebugPage()) return;
        window.clearInterval(this[`interval_${id}`]);
        this[`interval_${id}`] = setInterval(() => {
          if (isCloseAutoUpdateOnEasyDebugPage()) {
            return;
          }
          this.props.getComponentData && this.props.getComponentData(id, this.controller);
        }, (childInterval >= MAX_DELAY ? MAX_DELAY : childInterval) * 1000);
      }
    });
  };

  componentDidUpdate(prevProps) {
    if (!isEqual(this.props.autoUpdate, prevProps.autoUpdate)) {
      window.clearInterval(this.interval);
      this.setIntervalUpdate();
    }

    if (!isEqual(this.props.dataCallbackValues, prevProps.dataCallbackValues)) {
      this.props.getComponentData && this.props.getComponentData(this.props.id, this.controller);
    }

    this.props.childrenConfig.forEach((childConfig, i) => {
      const { id } = childConfig;
      if (
        !isEqual(this.props.childrenDataCallbackValues[i], prevProps.childrenDataCallbackValues[i])
      ) {
        this.props.getComponentData && this.props.getComponentData(id, this.controller);
      }
    });

    if (!isEqual(this.props.data, prevProps.data)) {
      this.emit(
        this.props.events.filter((d) => d.trigger === 'dataChange'),
        this.props.data,
      );
    }

    // 子组件的数据更新触发自定义事件
    this.props.childrenData.map((d, i) => {
      if (!isEqual(d.data, prevProps.childrenData[i] && prevProps.childrenData[i].data)) {
        const child = this.props.childrenConfig.find((o) => o.id === d.id);
        if (child) {
          this.emit(
            child.events.filter((d) => d.trigger === 'dataChange'),
            d.data,
          );
        }
      }
    });
  }

  componentWillUnmount() {
    window.clearInterval(this.interval);

    const { childrenConfig = [], id, base } = this.props;
    childrenConfig.map((d) => {
      const { id } = d;
      window.clearInterval(this[`interval_${id}`]);
    });
    this.controller && this.controller.abort();
    this.isComponentMounted = false;
    delete modules[`com_${id}_${this.random}`];
    delete moduleDependencies[`com_${id}_${this.random}`];
    dependencyModules[`${base.module_name}@${base.version}`] = dependencyModules[
      `${base.module_name}@${base.version}`
    ]?.filter((d) => d !== `com_${id}_${this.random}`);
  }

  handleEmitEvent = (payload) => {
    const { emitEvent } = this.props;
    if (emitEvent) {
      if (Array.isArray(payload.component)) {
        payload.component.map((d) => {
          emitEvent({
            _from: {
              componentId: this.props.id,
            },
            ...payload,
            component: d,
          });
        });
      } else {
        emitEvent({
          _from: {
            componentId: this.props.id,
          },
          ...payload,
        });
      }
    }
  };

  postMessage = (id, data, url) => {
    const iframe: any = document.getElementById(id);

    if (iframe) {
      iframe.contentWindow.postMessage(data, url);
    }
  };

  handleEmit = (eventName, data, componentId = this.props.id) => {
    const { callbackSend, debugListenStatus } = getDebugConfigFromSessionStorage();
    debugListenStatus === 'recording' &&
      callbackSend &&
      dispatchDebugScreenEvent<'info', 'callBack'>({
        type: 'info',
        id: this.props.id,
        name: this.props.name,
        dispatchFrom: 'callBack',
        debugMsg: {
          callbackValues: data,
        },
      });
    const { events, childrenEvents = [] } = this.props;

    if (!componentId || componentId === this.props.id) {
      this.emit(
        events.filter((d) => d.trigger === eventName),
        data,
      );
    } else {
      const childEvents = childrenEvents.find((d) => d.id === componentId);
      if (childEvents) {
        this.emit(
          childEvents.events.filter((d) => d.trigger === eventName),
          data,
        );
      }
    }
  };

  emit = (events: any[] = [], data: any) => {
    const { id, emitEvent } = this.props;
    const actions = getActions({ events, data }).filter((e) => e);
    function dispatchEvent(config) {
      if (defaultActions.find((o) => o.value === config.type)) {
        emitEvent({
          ...config,
          _from: {
            componentId: id,
          },
          controllers: [id],
        });
      } else {
        const event: any = new Event(`${config.type}_${config.component}`);
        event.data = config.data;
        event.dynamicData = config.dynamicData;
        document.dispatchEvent(event);
      }
    }

    actions.map((d) => {
      if (d?.animation?.delay > 0) {
        setTimeout(() => {
          dispatchEvent(d);
        }, d.animation.delay);
      } else {
        dispatchEvent(d);
      }
    });
  };

  render() {
    const { loadedScript, component } = this.state;

    if (!loadedScript) {
      return null;
    }

    const {
      id,
      width,
      height,
      top,
      left,
      uniqueTag,
      // hideDefault,
      data,
      configuration,
      bindedInteractionState,
      interactionCallbackValues,
      iState,
      // iActiveState,
      childrenConfig,
      childrenData,
      actions,
      events,
      onRelative,
    } = this.props;

    const Com = component;

    return (
      <ComErrorBoundary id={id} data={data}>
        <div
          id={uniqueTag}
          style={{
            position: 'absolute',
            pointerEvents: 'auto',
          }}>
          <Com
            id={id}
            width={width}
            height={height}
            top={top}
            left={left}
            iState={iState}
            bindedInteractionState={bindedInteractionState}
            data={data}
            configuration={configuration}
            actions={actions}
            events={events}
            childrenData={childrenData}
            childrenConfig={childrenConfig}
            callbackValues={interactionCallbackValues}
            emitEvent={this.handleEmitEvent}
            onRelative={onRelative}
            postMessage={this.postMessage}
            emit={this.handleEmit}
          />
        </div>
      </ComErrorBoundary>
    );
  }
}
