/**
 * MyBricks Opensource
 * https://mybricks.world
 * This source code is licensed under the MIT license.
 *
 * CheMingjun @2019
 * mybricks@126.com
 */

import React, { useMemo, useCallback, useLayoutEffect, useEffect, useState } from "react";

import RenderSlot from "./RenderSlot";
import { observable as defaultObservable, hijackReactcreateElement } from "./observable";

// import coreLib from '@mybricks/comlib-core'

import executor from './executor'
import { compareVersion } from "./utils";
import ErrorBoundary from "./ErrorBoundary";
import { setLoggerSilent } from "./logger";

const regAry = (comAray, comDefs) => {
  comAray.forEach(comDef => {
    if (comDef.comAray) {
      regAry(comDef.comAray, comDefs);
    } else {
      comDefs[`${comDef.namespace}-${comDef.version}`] = comDef;
    }
  })
}

export default function Main({ json, opts }: { json, opts: { env, events, libSourceCode, comDefs, comModules, observable, ref } }) {

  const comModules = useMemo(() => {
    console.log("opts.comModules", opts.comModules);
    return opts.comModules
  }, []);

  //所有组件定义
  const comDefs = useMemo(() => {
    // if (!opts.observable) {
    //   // 未传入observable，使用内置observable配合对React.createElement的劫持
    //   hijackReactcreateElement();
    // }

    if (opts.comDefs) {
      // regAry(coreLib.comAray, opts.comDefs);
      return opts.comDefs
    }

    // let comLibs = window["__comlibs_rt_"];//运行组件库，在preivew.html中引入

    // if (!comLibs) {
    //   comLibs = window["__comlibs_edit_"]//设计状态
    // }

    // if (!comLibs || !Array.isArray(comLibs)) {
    //   throw new Error(`组件库为空，请检查是否通过<script src="组件库地址"></script>加载了组件库运行时.`)
    // }

    // const comDefs = {}
    // comLibs.push(coreLib)
    // comLibs.forEach(lib => {
    //   const comAray = lib.comAray;

    //   if (comAray && Array.isArray(comAray)) {
    //     regAry(comAray, comDefs);
    //   }
    // })

    // return comDefs;
  }, [])

  const getComDef = useCallback((def) => {
    const rtn = comDefs[def.namespace + '-' + def.version]
    if (!rtn) {
      const ary = []
      for (let ns in comDefs) {
        if (ns.startsWith(def.namespace + '-')) {
          ary.push(comDefs[ns])
        }
      }

      if (ary && ary.length > 0) {
        ary.sort((a, b) => {
          return compareVersion(a.version, b.version)
        })

        const rtn0 = ary[0]
        console.warn(`【Mybricks】组件${def.namespace + '@' + def.version}未找到，使用${rtn0.namespace}@${rtn0.version}代替.`)

        return rtn0
      } else {
        console.log(comDefs)

        throw new Error(`组件${def.namespace + '@' + def.version}未找到，请确定是否存在该组件以及对应的版本号.`)
      }
    }
    return rtn
  }, [])

  //环境变量，此处可以定义连接器、多语言等实现
  const env = useMemo(() => {
    if (!!opts?.env?.silent) {
      setLoggerSilent();
    }
    return Object.assign({
      runtime: {},
      i18n(text: any) {
        return text
      },
      // canvasElement: document.body
    }, opts.env)
  }, [])

  const { slot } = json;

  // onError
  const onError = useMemo(() => {
    return (e) => {
      console.error(e);
    };
  }, []);
  // logger
  const logger = useMemo(() => {
    return {
      ...console,
      error: (e) => {
        console.error(e);
      },
    };
  }, []);

  //根据script生成context对象
  const [context, refs] = useMemo(() => {
    try {
      let refs
      const context = executor({
        comModules,
        json,
        getComDef,
        events: opts.events,
        env,
        ref(_refs) {
          refs = _refs
          if (opts.ref) {
            opts.ref(_refs)
          }
        },
        onError,
        logger
      }, {//////TODO goon
        observable: opts.observable || defaultObservable
      })

      return [context, refs]
    } catch (ex: any) {
      console.error(ex);
      throw new Error(`导出的JSON.script执行异常.`)
    }
  }, [])

  useLayoutEffect(() => {
    refs.run()
  }, [])

  return (
    <ErrorBoundary errorTip={`页面渲染错误`}>
      <RenderSlot
        env={env}
        slot={slot}
        getComDef={getComDef}
        getContext={context.get}
        __rxui_child__={!opts.observable}
        onError={onError}
        logger={logger}
      />
    </ErrorBoundary>
  )
}

