import React, { PureComponent } from 'react';
import ComErrorBoundary from './ComErrorBoundary';
import Animation from './Animation';
import { define, modules, moduleDependencies, dependencyModules } from 'utils/define';
import { getActions } from 'utils/interaction';
import defaultActions from 'constants/default-actions';
interface IProps {
  id: number;
  data: any;
  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[];
    autoUpdate: {
      isAuto: boolean;
      interval: number;
    } | null;
  }[];
  childrenData: {
    id: number;
    data: any;
    dataType: string;
  }[];
  edit: boolean;
  dataType: string;
  childrenEvents: any[];
  tenantId?: number;
  events: any[];
  bindedInteractionState: any;
  dataCallbackValues: { [x: string]: any };
  interactionCallbackValues: { [x: string]: any };
  childrenDataCallbackValues: any[];
  iState: any;
  iActiveState: any;
  modalEdit: number;
  actions: any[];
  updateComponentData?: (id: number) => void;
  onRelative?: (x, y) => void;
  emitEvent?: (param) => void;
  updateData?: (param) => void;
  updateAction?: (param) => void;
  updateConfig?: (param) => void;
  updateTrigger?: (param) => void;
  editId: number | string | null;
}

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

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,
      cameraEvent: null,
    };
  }

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

    const cameraEvent = new CustomEvent('cameraEvent', {
      bubbles: false,
      cancelable: true,
    });
    this.setState({
      cameraEvent,
    });
    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,
      );
    }

    // 初始化组件数据
    if (dataType !== 'static') {
      this.props.updateComponentData && this.props.updateComponentData(id);
    }

    childrenConfig.forEach((d, i) => {
      if (childrenData[i] && childrenData[i].dataType !== 'static') {
        this.props.updateComponentData && this.props.updateComponentData(d.id);
      }
    });
  }

  UNSAFE_componentWillReceiveProps(nextProps) {
    const { base, id, tenantId } = this.props;
    const { base: nextBase } = nextProps;
    if (base.version !== nextBase.version) {
      this.setState({
        loadedScript: false,
        component: null,
      });

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

  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 { 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 actions = getActions({ events, data }).filter((e) => e);
    const { emitEvent, id } = this.props;
    actions.map((d) => {
      if (emitEvent && defaultActions.find((o) => o.value === d.type)) {
        const o = {
          ...d,
          _from: {
            componentId: id,
          },
          controllers: [id],
        };
        if (d.animation?.delay > 0) {
          setTimeout(() => {
            emitEvent(o);
          }, d.animation.delay);
        } else {
          emitEvent(o);
        }
      } else {
        const event: any = new Event(`${d.type}_${d.component}`);
        event.data = d.data;
        event.dynamicData = d.dynamicData;
        if (d.animation?.delay > 0) {
          setTimeout(() => {
            document.dispatchEvent(event);
          }, d.animation.delay);
        } else {
          document.dispatchEvent(event);
        }
      }
    });
  };

  handleUpdateConfig = ({ id = this.props.id, type = 'config', payload ,isUpdateAllConfig = false}) => {
    if (this.props.edit && payload) {
      if (type === 'camera') {
        sessionStorage.setItem('camera', JSON.stringify(payload));
        document.dispatchEvent(this.state.cameraEvent);
      } else if (type === 'actions') {
        this.props.updateAction?.({ id, actions: payload ,isUpdateAllConfig});
      } else if (type === 'data') {
        this.props.updateData?.({ id, data: payload });
      } else if (type === 'triggers') {
        if (
          Array.isArray(payload) &&
          payload.every((d) => d.name !== undefined && d.value !== undefined)
        ) {
          this.props.updateTrigger?.({ id, triggers: payload });
        }
      } else if (type === 'config') {
        this.props.updateConfig?.({ id, config: payload });
      }
    }
  };

  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,
      onRelative,
      edit,
      modalEdit,
      actions,
      events,
      editId,
    } = this.props;
    const Com = component;
    const defaultState = {
      show: hideDefault ? false : true,
    };
    return (
      <ComErrorBoundary id={id} data={data} width={width} height={height} top={top} left={left}>
        <Animation
          id={id}
          iState={iState ? iState : defaultState}
          iActiveState={iActiveState}
          width={width}
          height={height}
          top={top}
          left={left}>
          <div
            id={uniqueTag}
            style={{
              position: 'absolute',
              pointerEvents: edit && !modalEdit ? 'none' : 'auto',
              zIndex:
                modalEdit === id || childrenData.some((d) => d.id === modalEdit) ? 1 : 'unset',
            }}>
            <Com
              editable={id === editId}
              id={id}
              edit={modalEdit === 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}
              updateConfig={this.handleUpdateConfig}
            />
          </div>
        </Animation>
      </ComErrorBoundary>
    );
  }
}
