import { resolveDataBinds, reportEvent } from '../common';
import { set as lodashSet } from 'lodash-es';
import { app } from '../../app/global-api';
import { runtime } from '../adapter';
import { untracked } from 'mobx';
import { generateWidgetAPIContext } from '../widgets';
import { IContext } from 'src/handlers/RenderWrapper';

import { _ACTIONS_KEY } from '@cloudbase/weda-client';
const DEFAULT_MAX_TIMEOUT = 10 * 1000;

class Event {
  type: string;
  detail: any;
  /**
   * 类别应当为 typeof UserWidget
   * 当前类别为 typeof $page.widgets.xxxx
   * 添加上报确定用量
   */
  currentTarget: any /* typeof UserWidget */;
  target?: any /* typeof UserWidget */;
  /**
   * 内部实现
   * 外部不应该进行访问
   */
  _isCapturePhase: boolean;
  origin: Event;
  originEvent?: React.SyntheticEvent;

  constructor({
    type = '',
    detail = undefined,
    currentTarget = undefined,
    target = undefined,
    _isCapturePhase = false,
    origin,
    originEvent = undefined,
  }: {
    type: string;
    currentTarget: any;
    target?: any;
    detail?: any;
    _isCapturePhase?: boolean;
    origin?: Event;
    originEvent?: React.SyntheticEvent;
  }) {
    function proxyWrapper(target, compareTarget, key) {
      try {
        return new Proxy(target, {
          get(target, p) {
            if (p !== 'id') {
              if (
                (key === 'currentTarget' || key === 'target') &&
                p !== '_userWidget' &&
                target[p] !== compareTarget?.[p]
              ) {
                // console.log(`@deprecated event.${key}.${String(p)}`);
                reportEvent(`event.${key}.${String(p)}`);
              }
            }
            return target[p];
          },
        });
      } catch (e) {
        return target;
      }
    }
    this.type = type;
    this.detail = detail;
    this.currentTarget = proxyWrapper(currentTarget, currentTarget?._userWidget, 'currentTarget');
    this.target = proxyWrapper(target, target?._userWidget, 'target');
    this.origin = origin;
    this.originEvent = originEvent;

    this._isCapturePhase = _isCapturePhase;
    return new Proxy(this, {
      get(target, prop) {
        switch (prop) {
          case 'name': {
            console.warn('[deprecated] event.name 将在未来版本放弃支持，请使用 event.type 替代');
            return target.type;
          }
          case 'origin':
          case 'target':
          case 'currentTarget': {
            return target[prop];
            break;
          }
        }

        return target[prop];
      },
    });
  }
}

type TEmitEventContext = Pick<IContext, 'scopeContext' | 'dataContext' | 'forContext'> & {
  $w?: any;
  pageId?: string;
  paramsContext?: Record<string, any>;
};
async function emitEvent(
  trigger,
  listeners = [],
  args: { event: Event; originalEvent?: React.SyntheticEvent },
  context: TEmitEventContext = {},
  syncCall = false,
) {
  const { $w, pageId = '' } = context || {};
  const targetListeners = listeners.filter((l) => l.trigger === trigger);
  const currentEvent = args?.event;

  function checkPageActive(pageId, currentPageContext, listener) {
    const { id, __internal__ } = currentPageContext || {};
    if (pageId && id) {
      if (pageId !== id || (__internal__ && !__internal__.active)) {
        if (!listener.trigger?.includes?.('onDataChange')) {
          console.error(`Action error: [${listener.trigger}:${listener.key}] 页面生命周期结束，链式调用中断`);
        }
        return false;
      }
    }
    return true;
  }

  async function processListener(listener) {
    // 当前非捕获Event，再判断冒泡行为
    if (!currentEvent?._isCapturePhase && listener.noPropagation) {
      args?.originalEvent?.stopPropagation && args?.originalEvent?.stopPropagation();
    }
    // 判断捕获的执行，只有执行的捕获与配置的捕获一致时。才会执行。
    // 为逻辑分支 额外新增处理

    const nextEvents = [
      new Event({
        ...currentEvent,
        type: '',
        detail: '',
        origin: currentEvent,
        originEvent: undefined,
      }),
    ];

    if ((listener?.isCapturePhase || false) === (currentEvent?._isCapturePhase || false)) {
      /**
       * 调用前置校验
       * 是否仍可调用方法
       */
      if (!checkPageActive(pageId, $w?.page, listener)) {
        return;
      }

      if (listener?.sourceKey === 'platform:utils.If') {
        try {
          const res = await invokeListener(listener, currentEvent, context);
          nextEvents[0].type = `${listener.key}.${res ? 'success' : 'fail'}`;
        } catch (e) {
          nextEvents[0].type = `${listener.key}.fail`;
        }
        nextEvents[0].detail = currentEvent.detail;
      } else {
        try {
          const res = await invokeListener(listener, currentEvent, context);
          nextEvents[0].type = `${listener.key}.success`;
          nextEvents[0].detail = res;

          if (listener?.sourceKey === `platform:${_ACTIONS_KEY.showModal}`) {
            const showModalNextEvent = new Event({
              ...currentEvent,
              type: '',
              detail: res,
              origin: currentEvent,
              originEvent: undefined,
            });

            if (res.cancel) {
              showModalNextEvent.type = `${listener.key}.cancel`;
            } else if (res.confirm) {
              showModalNextEvent.type = `${listener.key}.confirm`;
            }

            if (showModalNextEvent.type) {
              nextEvents.push(showModalNextEvent);
            }
          }
        } catch (e) {
          if (e.message) {
            e._target = `${currentEvent?.currentTarget?.id ? `组件${currentEvent?.currentTarget?.id}` : ''}[${
              listener.trigger
            }事件，响应Id ${listener.key}]`;
          }

          nextEvents[0].type = `${listener.key}.fail`;
          nextEvents[0].detail = e;

          if (!syncCall) {
            // 之前 invoke 内部catch 了错误，不会抛错
            console.error(`Action error:`, generateErrorDebugMessage(e), e.stack);
          }
        }
      }
      /**
       * 调用后置校验
       * 是否触发后置事件
       */
      if (checkPageActive(pageId, $w?.page, listener)) {
        let emitEventPromise = null;
        nextEvents.forEach((nextEvent) => {
          emitEventPromise = emitEvent(
            nextEvent.type,
            listeners,
            {
              ...args,
              event: nextEvent,
            },
            context,
            syncCall,
          );
        });
        return syncCall ? emitEventPromise : undefined;
      } else if (syncCall) {
        // 页面不再 active 且同步调用，此时抛出错误中断
        throw new Error(`[${listener.trigger}:${listener.key}] 页面生命周期结束，链式调用中断`);
      }
    }
  }

  if (!targetListeners.length && typeof trigger === 'string' && /\.fail$/.test(trigger)) {
    const [preEventKey] = trigger.split('.') || [];
    const preListener = listeners.find((item) => item.key === preEventKey);
    /**
     * if 的 否分支， 不进行错误提醒
     */
    if (preListener.sourceKey !== 'platform:utils.If') {
      if (!syncCall) {
        if ($w?.env?.type !== 'production') {
          app.showModal({
            title: `事件响应失败`,
            content: generateErrorDebugMessage(currentEvent?.detail),
            showCancel: false as any,
          });
        } else {
          app.showToast({
            title: `事件响应失败：${generateErrorDebugMessage(currentEvent?.detail)}`,
            icon: 'error',
          });
        }
      } else {
        throw currentEvent?.detail;
      }
    }
  } else {
    const promise = Promise.all(
      targetListeners.map((listener) => {
        return processListener(listener).then(
          (res) => ({ data: res, error: null }),
          /**
           * 按说 processListener 不会报错
           */
          (error) => ({ data: null, error }),
        );
      }),
    );
    if (syncCall) {
      const listenerRes = await promise;
      const res = [];
      for (const item of listenerRes) {
        if (item.error) {
          throw item.error;
        } else {
          res.push(item.data);
        }
      }
      return res.length ? res[0] : currentEvent.detail;
    }
  }
}

function generateErrorDebugMessage(error) {
  let message = error?.message || '';
  try {
    if (!message) {
      message = JSON.stringify(error);
    }
  } catch (e) {}
  return `${error?._target ? `${error?._target}:` : ''}${message}`;
}

export function generateEmit(
  $w,
  currentWidget = undefined, // page 节点暂未生成
  injectContext = undefined,
  extraListeners = [],
  extraForContext = {},
) {
  return (
    trigger,
    listeners = [],
    eventData,
    forItems,
    originalEvent: React.SyntheticEvent,
    scopeContext,
    dataContext,
    pageId = '',
    syncCall = false,
  ) => {
    listeners = listeners.concat(extraListeners);

    if (injectContext?.emit) {
      return injectContext?.emit(trigger, listeners, eventData, forItems, originalEvent, scopeContext, dataContext);
    } else {
      const event = new Event({
        detail: eventData,
        type: trigger,
        target: currentWidget,
        currentTarget: currentWidget,
        originEvent: originalEvent,
      });
      const forContext = {
        ...forItems,
        ...extraForContext,
      };

      return emitEvent(
        event.type,
        listeners,
        {
          event,
        },
        {
          pageId,
          forContext,
          scopeContext,
          dataContext,
          $w: untracked(() => {
            return generateWidgetAPIContext($w, currentWidget, forContext, scopeContext);
          }),
        },
        syncCall,
      );
    }
  };
}

function invokeListener(listener, event: Event, context: TEmitEventContext) {
  const { $w, forContext, scopeContext = {}, dataContext, paramsContext = {} } = context || {};
  const { key, instanceFunction, data = {}, dataBinds = {} } = listener;
  if (!instanceFunction) {
    try {
      // @ts-ignore
      window._aegis?.error?.('SYSTEM_ERROR: listener not found', listener);
      console.error('$page current hander', runtime?.['$page']?.handler);
    } catch (e) {}
    throw new Error(`${key} handler 未找到'`);
  }
  // ToDo resolve databinds
  const action = instanceFunction;
  const resolvedData = mergeDynamic2StaticData(data, dataBinds, {
    $w,
    forContext,
    codeContext: { event },
    scopeContext,
    dataContext,
    paramsContext,
  });

  return action({
    data: resolvedData,
    event,
    forItems: forContext,
    $w,
  });
}

export function mergeDynamic2StaticData(
  staticData,
  dataBinds,
  context: {
    $w: any;
    forContext?: IContext['forContext'];
    codeContext?: {
      $WEAPPS_COMP?: any;
      event?: Event;
    };
    scopeContext?: IContext['scopeContext'];
    dataContext?: IContext['dataContext'];
    paramsContext?: Record<string, any>;
  },
) {
  const { forContext = {}, codeContext = {}, scopeContext = {}, dataContext = {}, $w, paramsContext = {} } = context;
  const resolvedData = {
    ...staticData,
  };
  const resolvedDataBinds = resolveDataBinds(
    dataBinds,
    forContext,
    codeContext,
    scopeContext,
    true,
    dataContext,
    $w,
    paramsContext,
  );

  // eslint-disable-next-line no-restricted-syntax
  for (const key in resolvedDataBinds) {
    if (resolvedDataBinds[key] && resolvedDataBinds[key].__type === 'scopedValue') {
      try {
        lodashSet(resolvedData, key, resolvedDataBinds[key].getValue(scopeContext));
      } catch (e) {
        lodashSet(resolvedData, key, '');
      }
    } else {
      lodashSet(resolvedData, key, resolvedDataBinds[key]);
    }
  }

  return resolvedData;
}
