import * as React from 'react';
import { observer } from 'mobx-react-lite';
import { observable, autorun, reaction, runInAction } from 'mobx';
import { AppRender } from '/src/handlers/RenderWrapper';
import {
  createWidgets,
  retryDataBinds,
  WidgetsContext,
  bindFuncObj,
  createComputed,
  resolveComponentProps,
} from '/src/utils/index';
import getStateFn from './lowcode/state.js';
import computed from './lowcode/computed.js';
import lifecycle from './lowcode/lifecycle.js';
// import i18n from '@tcwd/weapps-sdk/lib/i18n'

import componentAPI from './lowcode/index';
import { get } from 'lodash-es';

import handler$onBlur from './lowcode/handler/onBlur.js';

import handler$onChange from './lowcode/handler/onChange.js';

import handler$onConfirm from './lowcode/handler/onConfirm.js';

import handler$onFocus from './lowcode/handler/onFocus.js';

import handler$getForm from './lowcode/handler/getForm.js';

// Import Components outof sandbox

import gsdH5React from '/src/libraries/gsd-h5-react@0.0.70/web/index.js';
const { Input: GsdH5ReactInput } = gsdH5React.components;

import CLOUDBASESTANDARDFormItem from '/src/libraries/CLOUDBASE_STANDARD@1634202837896/components/FormItem';

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

import './lowcode/style.css';

const libCode = 'CLOUDBASE_STANDARD';

class CompositeCompWrapper extends React.Component {
  $WEAPPS_COMP = {};

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

  constructor(props) {
    super(props);

    this.compConfig = {
      componentType: 'formField',
    };

    // Import Components in sandbox

    this.virtualFields = Object.assign({}, props.pageVirtualFields || {}, {
      'gsd-h5-react:Input': (props) => (
        <GsdH5ReactInput {...resolveComponentProps(props, 1)} />
      ),

      'CLOUDBASE_STANDARD:FormItem': (props) => (
        <CLOUDBASESTANDARDFormItem {...resolveComponentProps(props, 0)} />
      ),
    });

    // 挂载给到 $comp.props.events 使用
    this.events = ['change', 'focus', 'blur', 'confirm', 'clear'].reduce(
      (obj, trigger) => {
        obj[trigger] = (eventData, domEvent) => {
          this.props.emit(trigger, eventData, domEvent);
          this.node._listeners &&
            this.node._listeners.emit(trigger, eventData, domEvent);
        };
        return obj;
      },
      {}
    );
    this.handler = this.$WEAPPS_COMP.handler = {
      onBlur: handler$onBlur.bind(this),

      onChange: handler$onChange.bind(this),

      onConfirm: handler$onConfirm.bind(this),

      onFocus: handler$onFocus.bind(this),

      getForm: handler$getForm.bind(this),
    };
    const widgetContext = {
      id3: {
        type: 'text',
        password: false,
        _visible: true,
        style: {},
        classList: [],
        widgetType: 'gsd-h5-react:Input',
        _parentId: 'id1',
      },
      id1: {
        rules: [
          {
            format: 'url',
            message: 'url校验失败',
          },
        ],
        layout: '',
        validateStatus: 'success',
        submitFormatter: '',
        validateTrigger: 'onChange',
        displayFormatter: '',
        _visible: true,
        style: {},
        classList: [],
        widgetType: 'CLOUDBASE_STANDARD:FormItem',
      },
    };
    const dataBinds = (this._dataBinds = {
      id3: {
        name: () => this.$WEAPPS_COMP.props.data.name,
        focus: () => this.$WEAPPS_COMP.props.data.focus,
        label: () => this.$WEAPPS_COMP.props.data.label,
        layout: (forItems) => {
          const $for = forItems;
          return this.$WEAPPS_COMP.computed.layout;
        },
        disabled: () => this.$WEAPPS_COMP.props.data.disabled,
        maxLength: () => this.$WEAPPS_COMP.props.data.maxLength,
        placeholder: () => this.$WEAPPS_COMP.props.data.placeholder,
        defaultValue: () => this.$WEAPPS_COMP.props.data.value,
        labelVisible: () => this.$WEAPPS_COMP.props.data.labelVisible,
        requiredFlag: (forItems) => {
          const $for = forItems;
          return (
            this.$WEAPPS_COMP.props.data.required &&
            this.$WEAPPS_COMP.props.data.requiredFlag
          );
        },
      },
      id1: {
        name: () => this.$WEAPPS_COMP.props.data.name,
        required: () => this.$WEAPPS_COMP.props.data.required,
      },
    });
    this.componentSchema = {
      type: 'object',
      properties: {
        id1: {
          key: 'id1',
          'x-component': 'cloudbase_standard:formitem',
          'x-props': {
            sourceKey: 'CLOUDBASE_STANDARD:FormItem',
            customDataForm: {},
            staticResourceAttribute: [],
            classNameList: ['wa-comp-CLOUDBASE_STANDARD-FormUrl'],
          },
          'x-index': 0,
          emitEvents: ['validator'],
          properties: {
            contentSlot: {
              key: 'contentSlot',
              properties: {
                id3: {
                  key: 'id3',
                  'x-component': 'gsd-h5-react:input',
                  'x-props': {
                    sourceKey: 'gsd-h5-react:Input',
                    customDataForm: {},
                    staticResourceAttribute: [],
                    listenerInstances: [
                      {
                        key: 'wagdi13tb1o8',
                        trigger: 'change',
                        instanceFunction: this.handler.onChange.bind(this),
                        data: {
                          target: '',
                        },
                      },
                      {
                        key: 'waf9g4o4sf7g',
                        trigger: 'focus',
                        instanceFunction: this.handler.onFocus.bind(this),
                        data: {
                          target: '',
                        },
                      },
                      {
                        key: 'wauqr5v4guvg',
                        trigger: 'blur',
                        instanceFunction: this.handler.onBlur.bind(this),
                        data: {
                          target: '',
                        },
                      },
                      {
                        key: 'wa8hq0fushq8',
                        trigger: 'confirm',
                        instanceFunction: this.handler.onConfirm.bind(this),
                        data: {
                          target: '',
                        },
                      },
                      {
                        key: 'wat14adoc6n',
                        trigger: 'clear',
                        instanceFunction: function ({ data, event, forItems }) {
                          event.detail.data = data;
                          this.props.emit('clear', event.detail);
                        }.bind(this),
                        data: {
                          target: '',
                        },
                      },
                    ],
                  },
                  'x-index': 0,
                  emitEvents: ['change', 'focus', 'blur', 'confirm', 'clear'],
                },
              },
            },
          },
        },
      },
    };

    const defaultProps = {
      name: 'formUrl',
      type: 'text',
      focus: false,
      label: '网址',
      value: '',
      layout: '',
      disabled: false,
      required: false,
      clearable: true,
      maxLength: 140,
      placeholder: '请输入网址',
      requiredMsg: '该项为必填项',
      labelVisible: true,
      requiredFlag: false,
    };
    this.propsData = observable(
      Object.assign({}, defaultProps, this.props.data || {})
    );
    this.$WEAPPS_COMP.lib = { const: constObj, tools: toolsObj };
    /*
    this.i18n = this.$WEAPPS_COMP.i18n = {
      ...i18n,
      t (key, data) {
        return i18n.t(libCode + ':' + key, data)
      },
    }
    */
    this.$WEAPPS_COMP.props = {
      ...this.props,
      events: this.events,
      data: this.propsData,
    };

    this.state = this.$WEAPPS_COMP.state = observable(getStateFn.bind(this)());
    this.computed = this.$WEAPPS_COMP.computed = createComputed(computed, this);
    this.node = this.$WEAPPS_COMP.node = this.createWidgetNode(this) || {};
    this.initPublicMethods();

    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.widgets[widgetId].ownerWidget = this.node
      // this.widgets[widgetId].getOwnerWidget = () => this.node
    });

    this.pageListenerInstances = [];
    this.createCompAPI(this);
  }

  // publicMthods 挂载内部，使用 $comp.api。挂载到外部使用 $comp.node.api
  initPublicMethods() {
    const publicMethods = componentAPI.publicMethods || {};
    Object.keys(publicMethods).map((fnName) => {
      const bindFunc = publicMethods[fnName].bind(this);
      this[fnName] = bindFunc;
      this.$WEAPPS_COMP[fnName] = bindFunc;
      if (this.node) this.node[fnName] = bindFunc;
    });
  }

  // 创建自身节点
  createWidgetNode(compThis) {
    // 当为数组时，需要判断自己属于 widgets 的哪一项
    const { compositeParent, forIndexes, id } = compThis.props;
    let widgetData = compositeParent.$WEAPPS_COMP
      ? compositeParent.$WEAPPS_COMP.widgets[id]
      : compositeParent.$page.widgets[id];
    if (Array.isArray(widgetData)) {
      widgetData = widgetData.length > 0 ? get(widgetData, forIndexes) : {};
    }
    widgetData = widgetData || {};
    Object.assign(
      widgetData,
      {
        getConfig: () => compThis.compConfig,
      },
      bindFuncObj(componentAPI.publicMethods || {}, compThis)
    );

    return widgetData;
  }

  createCompAPI(compThis) {
    compThis.$WEAPPS_COMP = {
      _dataBinds: compThis._dataBinds,
      compConfig: compThis.compConfig,
      widgets: compThis.widgets,
      node: compThis.node,
      handler: compThis.handler,
      lib: { const: constObj, tools: toolsObj },
      // i18n: compThis.i18n,
      get props() {
        return {
          ...compThis.props,
          events: compThis.events,
          data: compThis.propsData,
        };
      },
      get state() {
        return compThis.state;
      },
      get computed() {
        return compThis.computed;
      },
    };
  }

  initWatchMethods() {
    const {
      watch = {},
      watchState = {},
      watchWidget = {},
      watchEffects = {},
    } = componentAPI;
    const _this = this;
    const weappInst = _this.$WEAPPS_COMP;

    // # watch effect
    this.watchEffectDisposers = Object.keys(watchEffects).map((fnName) => {
      return autorun(watchEffects[fnName].bind(this));
    });
    const disposers = this.watchEffectDisposers;

    // # watch props
    Object.keys(watch).map((key) =>
      runWatcher(parseWatcher(watch[key]), weappInst.props.data, key, 'watch')
    );

    // # watch state
    Object.keys(watchState).map((key) =>
      runWatcher(
        parseWatcher(watchState[key]),
        weappInst.state,
        key,
        'watchState'
      )
    );

    // # watch widgets
    Object.keys(watchWidget).map((key) =>
      runWatcher(
        parseWatcher(watchWidget[key]),
        weappInst.widgets,
        key,
        'watchWidgets'
      )
    );

    function runWatcher({ handler, immediate } = {}, target, key, label) {
      if (!handler) {
        console.error(
          `Invalid ${label}(${key}) of ${weappInst.node.widgetType}, watch must a function or {handler: function}`
        );
        return;
      }
      const disposer = reaction(() => get(target, key), handler.bind(_this), {
        fireImmediately: immediate,
      });
      disposers.push(disposer);
    }
  }

  componentDidMount() {
    this.initPublicMethods();
    this.initWatchMethods();

    lifecycle.onAttached && lifecycle.onAttached.bind(this)();
    lifecycle.onReady && lifecycle.onReady.bind(this)();
  }

  componentWillUnmount() {
    if (this.watchEffectDisposers) {
      this.watchEffectDisposers.forEach((disposer) => disposer());
      this.watchEffectDisposers = null;
    }
    this.node._listeners && this.node._listeners.clear();
    lifecycle.onDetached && lifecycle.onDetached.bind(this)();
  }

  render() {
    return (
      <WidgetsContext.Provider value={{ parent: this }}>
        <AppRender
          className={this.props.className}
          virtualFields={this.virtualFields}
          componentSchema={this.componentSchema}
          codeContext={this}
        />
      </WidgetsContext.Provider>
    );
  }
}

CompositeCompWrapper.contextType = WidgetsContext;

export default observer((props) => (
  <CompositeCompWrapper {...props}></CompositeCompWrapper>
));

function parseWatcher(watcher) {
  if (!watcher) return;
  if (watcher instanceof Function) {
    return { handler: watcher, immediate: false };
  }
  const { handler, immediate = false } = watcher;
  if (!(handler instanceof Function)) return;
  return { handler, immediate };
}
