import { compile, createIO, I_Frame, I_Node, I_Pin, I_Runner } from '@fangzhou/compiler-js';
import React, { FC, ReactNode, useMemo } from 'react';
import { rawToProxy } from '@fangzhou/rxui-lite';
import { RenderCom } from './RenderCom';
type T_LogItem = { catelog: string; content: string; isBaseType: boolean; focus: Function; blur: Function };

export function RenderReact({
  mainModule,
  comDefs,
  inputParams,
  inputs,
  outputs,
  env,
  events,
  layoutComs,
  logs,
}: {
  mainModule: { frame: I_Frame; slot: {} };
  comDefs: { [nsAndVersion: string]: Function };
  inputParams?;
  inputs?: { [id: string]: (fn: Function) => void };
  outputs?: { [id: string]: Function };
  env: {
    fetch?: (url: string) => Promise<any>;
  };
  events: any[];
  layoutComs: {
    Wrapper: FC;
    Slot: FC;
    ErrorBoundary: FC;
  };
  logs: {
    info: (item: T_LogItem) => void;
    error: (item: T_LogItem) => void;
  };
}) {
  const nComDefs = Object.assign({}, comDefs);

  const RT_MAPS = {};

  const { frame, slot } = mainModule;
  const runner: I_Runner = compile(frame, {
    node(node: I_Node) {
      return {
        render(frames: {}, curScope) {
          const io = createIO(
            node,
            {
              output() {
                //igonreObservableBefore()//TODO 待测试
              },
            },
            curScope,
          );

          RT_MAPS[node.id] = { frames, io };

          const rtDef = node.def;
          const rtType = rtDef.rtType;

          if (rtType && rtType.match(/js/gi)) {
            //逻辑组件
            const rtCfg = {
              get curModule() {
                const module = node.parent.parent;
                if (module) {
                  const frame = node.parent;
                  const outPins = frame.outputPins;
                  const outputs = {};

                  if (Array.isArray(outPins)) {
                    outPins.forEach((pin) => {
                      outputs[pin.id] = (val, callback) => {
                        pin?._exe(curScope, val, callback);
                      };
                    });
                  }
                  return {
                    outputs,
                  };
                }
              },
            };

            const ns = rtDef.namespace + '@' + rtDef.version;
            const comRt = nComDefs[ns];
            if (comRt && typeof comRt === 'function') {
              comRt({
                data: node.model.data,
                inputs: io.inputs,
                outputs: io.outputs,
                env: Object.assign({ runtime: rtCfg }, env || {}),
              });
            } else {
              throw new Error(`未找到组件(${ns})`);
            }
          }

          return io;
        },
      };
    },
    pin(pin: I_Pin) {
      //处理Pin extension
      const comRT = pin.parent ? pin.parent : void 0;

      return {
        exe(value: any) {
          if (pin.type.match(/^ext$/gi)) {
            if (pin.direction.match(/^input|inner-output$/gi)) {
              const styleProxy = rawToProxy.get(pin.parent.model.style);
              if (pin.hostId === 'show') {
                if (styleProxy) {
                  styleProxy.display = 'block';
                } else {
                  pin.parent.model.style.display = 'block';
                }
              } else if (pin.hostId === 'hide') {
                if (styleProxy) {
                  styleProxy.display = 'none';
                } else {
                  pin.parent.model.style.display = 'none';
                }
              }

              return false;
            }

            return false;
          }

          if (pin.direction.match(/^output|inner-input$/gi)) {
            if (comRT) {
              const evts = comRT.model?.outputEvents;
              if (evts) {
                const eAry = evts[pin.hostId];
                if (eAry && Array.isArray(eAry)) {
                  const activeEvt = eAry.find((e) => e.active);
                  if (activeEvt) {
                    if (activeEvt.type === 'none') {
                      return false;
                    }
                    if (activeEvt.type === 'defined') {
                      return;
                    }

                    if (Array.isArray(events)) {
                      const def = events.find((ce) => {
                        if (ce.type === activeEvt.type) {
                          return ce;
                        }
                      });
                      if (def && typeof def.exe === 'function') {
                        def.exe({ options: activeEvt.options }, value);
                      }
                    }
                    return false;
                  }
                }
              }
            }
          }

          const strVal = typeof value === 'object' && value ? JSON.stringify(value) : String(value);

          if (comRT) {
            if (logs) {
              if (typeof logs.info === 'function') {
                setTimeout((v) => {
                  logs.info({
                    catelog: `程序运行 ${comRT.title} | ${pin.title} ${
                      pin.direction == 'input' || pin.direction == 'inner-input' ? '传入' : '传出'
                    }`,
                    content: strVal,
                    isBaseType: !value || typeof value !== 'object',
                    focus() {
                      comRT._focus = true;
                    },
                    blur() {
                      comRT._focus = void 0;
                    },
                  });
                });
              }
            }
          }
        },
      };
    },
  } as any);

  runner.run()({
    inputParams,
    inputs,
    outputs,
  });

  const { comAry } = slot;
  const ErrorBoundary = layoutComs.ErrorBoundary;
  const fixedComs: ReactNode[] = [];
  const coms: ReactNode[] = [];

  comAry?.map((node: I_Node) => {
    if (node.model.style.position === 'fixed') {
      fixedComs.push(
        <ErrorBoundary>
          <RenderCom
            events={events}
            node={node}
            comDefs={nComDefs}
            env={{ ...env }}
            rtMaps={RT_MAPS}
            layoutComs={layoutComs}
          />
        </ErrorBoundary>,
      );
    } else {
      coms.push(
        <ErrorBoundary>
          <RenderCom
            events={events}
            node={node}
            comDefs={nComDefs}
            env={{ ...env }}
            rtMaps={RT_MAPS}
            layoutComs={layoutComs}
          />
        </ErrorBoundary>,
      );
    }
  });
  return [coms, fixedComs];
}
