import * as React from "react";
import { observer } from "mobx-react-lite";
import { observable, runInAction } from "mobx";
import { get } from 'lodash'
import { AppRender, useComponentProps, OwnerComponentContext } from "@cloudbase/lowcode-render";
import { createWidgets, retryDataBinds, createComputed} from '../../../../utils/index'
import getStateFn from "./lowcode/state.js";
import computed from "./lowcode/computed.js";
import lifecycle from "./lowcode/lifecycle.js";

const app = new Proxy({}, {
  get: function(obj, prop){ return window.app ? window.app[prop] : undefined},
  set: function(obj, prop, value){ if(window.app) {return  window.app[prop] = value} else {return undefined } }
});
const $app = new Proxy({}, { get: function(obj, prop){ return app[prop] }});

<% handlersImports.forEach(handler => { %>
import handler$<%= handler.name %> from "./lowcode/handler/<%= handler.name %>.js";
<% }) %>

// Import Components outof sandbox
<%= componentImports %>

import * as constObj from '../../libCommonRes/const'
import * as toolsObj from '../../libCommonRes/tools'

import "./lowcode/style.css";

class CompositeCompWrapper extends React.Component {
  static contextType = OwnerComponentContext

  $WEAPPS_COMP = null

  componentDidUpdate() {
    runInAction(() => {
      const { data, id } = this.props
      for(const prop in data) {
        // 更新 propsData
        if (typeof data[prop] !== 'function') {
          this.propsData[prop] = data[prop]
        }
      }
    })
  }


  constructor(props) {
    super(props);

    this.createCompAPI(this)

    this.compConfig = this.$WEAPPS_COMP.compConfig = <%= JSON.stringify(compConfig, null, 2) %>

    // Import Components in sandbox
    <% useComponents.forEach(compItem => {%>
    <% if(isSandbox){ %>
    const <%= compItem.var %> = window["@weapps-materials-main-<%= compItem.moduleName %>"].components["<%= compItem.name %>"];
    <% } %>
    <%}) %>

    this.virtualFields = Object.assign({}, props.pageVirtualFields || {}, {
    <% useComponents.forEach(compItem => {%>
      "<%= compItem.key %>": React.forwardRef((props,ref) => {
        const processedProps = useComponentProps(props,  <%= compItem.isPlainProps? 1:0 %>);
        return <<%= compItem.var %> {...processedProps} ref={ref}/>
      }),
    <%}) %>
    });

    // 挂载给到 $comp.props.events 使用
    this.events = (<%= emitEvents %>).reduce((obj, trigger) => {
      obj[trigger] = (eventData, options) => {
        this.props.emit(trigger, eventData, options?.originEvent)
      };
      return obj;
    }, {});
    // create widgets 需要访问，因此初始化要早于 widget
    this.handler = this.$WEAPPS_COMP.handler = {
      <% handlersImports.forEach(handler => { %>
      <%= handler.name %>: handler$<%= handler.name %>.bind(this),
      <% }) %>
    };
    this.componentSchema = <%= componentSchema %>;
    const widgetContext = <%= widgets %>
    const dataBinds = this._dataBinds = this.$WEAPPS_COMP._dataBinds = <%= dataBinds %>
    const defaultProps = <%= JSON.stringify(defaultProps, null, 2) %>
    this.propsData = observable(Object.assign({}, defaultProps, this.props.data || {}))
    // create widgets 需要访问，因此初始化要早于 widget
    this.state = observable(getStateFn.bind(this)())
    this.computed = createComputed(computed, this)
    this.node = this.$WEAPPS_COMP.node = this.createWidgetNode(this) || {}
    this.widgets = this.$WEAPPS_COMP.widgets = createWidgets(widgetContext, dataBinds)
    // widgets 内的 dataBinds 可能需要关联 widgets，需要重新执行 dataBinds
    retryDataBinds()
    Object.keys(this.widgets || {}).forEach(widgetId => {
      // 将实例 ownerWidget 挂到内部组件上。内部组件就可以通过 $comp.node.ownerWidget 获取到所在的组件实例
      const node = this.widgets[widgetId]
      if (Array.isArray(node)) {
        node.forEach(item => {
          item.ownerWidget = this.node;
          item.getOwnerWidget = () => this.node;
        })
      }
      else {
        node.ownerWidget = this.node
        node.getOwnerWidget = () => this.node
      }
    })

    this.pageListenerInstances = [];
  }

  // 创建自身节点
  createWidgetNode(compThis) {
    // 当为数组时，需要判断自己属于 widgets 的哪一项
    const {
      $node
    } = compThis.props
    let widgetData = $node || {}
    Object.assign(widgetData, {
      getConfig: () => compThis.compConfig,
      getOwnerWidget: () => compThis.node.ownerWidget
    })

    return widgetData
  }

  createCompAPI(compThis) {
    const { forwardRef } = compThis.props || {};
    compThis.$WEAPPS_COMP = {
      __internal__: {
        $w: new Proxy(
          {},
          {
            get(_, prop) {
              // 尝试代理页面级别组件实例
              let pageWidget = get(compThis.$WEAPPS_COMP, `widgets.${prop}`);
              if (pageWidget) {
                return pageWidget._userWidget;
              }

              return window.app?.__internal__?.$w?.[prop];
            },
          }
        ),
      },
      _dataBinds: compThis._dataBinds,
      compConfig: compThis.compConfig,
      widgets: compThis.widgets,
      node: compThis.node,
      handler: compThis.handler,
      lib: { const: constObj, tools: toolsObj },
      get props() {
        const { forwardRef, ...restProps } = compThis.props || {};
        return {...restProps, events: compThis.events, data: compThis.propsData }
      },
      get state() {
        return compThis.state
      },
      get computed() {
        return compThis.computed
      },
      get _instanceRef() {
        return forwardRef
      },
      get methods() {
        return forwardRef.current?.methods
      },
      set methods(value) {
        if(!forwardRef.current) {
          forwardRef.current = {}
        }
        forwardRef.current.methods = value
        return value
      }
    };
  }

  componentWillMount() {
    /**
     * @important
     * class 和 function 混用
     * 发现 子function useEffect 竟然比 父 didmount 要晚
     * 不复合期望,保留旧版调用
     */
    lifecycle.onAttached?.bind?.(this)?.();
  }

  render() {
    return (
      <CompositeCompRender
        className={this.props.className}
        /**
         * AppRender 是 React.memo 的
         * 因此需要 props 主动传入响应变化
         */
        style={this.props.style}
        virtualFields={this.virtualFields}
        componentSchema={this.componentSchema}
        codeContext={this}
        lifecycle={lifecycle}
      />
    );
  }
}

function CompositeCompRender({
  className = '',
  style,
  virtualFields,
  componentSchema,
  codeContext,
  lifecycle,
}) {
  const { attached } = React.useContext(OwnerComponentContext);
  const [mount, setMounted] = React.useState(false);
  React.useEffect(() => {
    setMounted(true);
    return () => {
      if(codeContext.widgets._disposers){
        codeContext.widgets._disposers.forEach(disposer => disposer())
        codeContext.widgets._disposers = null
      }
      lifecycle.onDetached?.bind?.(codeContext)?.();
      codeContext.$WEAPPS_COMP._instanceRef.current = undefined
      setMounted(false);
    };
  }, []);

  React.useEffect(() => {
    if (attached) {
      lifecycle.onReady?.bind?.(codeContext)?.();
    }
  }, [attached]);

  const ownerComponentContext = React.useMemo(() => {
    return { attached: mount };
  }, [mount]);

  return (
    <OwnerComponentContext.Provider value={ownerComponentContext}>
      <AppRender
        className={className}
        style={style}
        virtualFields={virtualFields}
        componentSchema={componentSchema}
        codeContext={codeContext}
      />
    </OwnerComponentContext.Provider>
  );
}

export default observer((props, _ref) => (
  <CompositeCompWrapper {...props}  forwardRef={_ref}></CompositeCompWrapper>
), { forwardRef: true });

