import { observable } from 'mobx';
import { createComputed, createEventHandlers, checkAuth } from './util';
import { createWidgets, createInitData, disposeWidget, ID_SEPARATOR, getWidget } from './widget';
import mergeRenderer from './merge-renderer';
import {
  createDataset,
  EXTRA_API,
  createStateDataSourceVar,
  generateParamsParser,
  setConfig,
} from '../datasources/index';
import { runWatchers } from './watch';
import { getMpEventHandlerName } from './util';

export function createPage(
  id,
  uuid,
  widgetProps,
  index = {},
  lifecycle,
  pageState,
  pageComputed,
  evtListeners,
  dataBinds,
  app,
  handler,
  pageContext = {},
  pageAttributes,
) {
  const evtHandlers = createEventHandlers(evtListeners);

  function extractLifecycles() {
    const result = {};
    Object.keys(lifecycle).map((name) => {
      result[name] = function () {
        return lifecycle[name].apply(this._getInstance(), arguments);
      };
    });

    result['onShareAppMessage'] = (res) => {
      if (res?.from === 'button' && res?.target?.dataset?.weda_share_info) {
        return res?.target?.dataset?.weda_share_info;
      } else if (res?.from === 'menu' && pageAttributes?.appShareMessage) {
        let { enable, pageId, params, imageUrl, title } = pageAttributes.appShareMessage;
        if (enable) {
          pageId = pageId ? pageId.replace(/^(\.)?\//, '') : pageId;
          let realPath =
            `/pages/${pageId}/index` +
            (params ? '?' + params.map((pair) => pair.key + '=' + pair.value).join('&') : '');
          return {
            path: realPath,
            imageUrl,
            title,
          };
        }
      }
      try {
        return lifecycle?.['onShareAppMessage']?.() || {};
      } catch (error) {
        console.log(error);
        return {};
      }
    };
    return result;
  }

  return Component({
    _componentType: 'page',
    data: createInitData(widgetProps, dataBinds),
    lifetimes: {
      attached() {
        const $page = this._getInstance();
        this._pageActive = true;
        /**
         * @deprecated
         * 使用 behavior 的方式挂载methods
         */
        this.__mnt__ = (e) => {
          const widget = getWidget($page.widgets, e.target.id);
          widget._methods = e.detail.methods;
        };
        /**
         * @deprecated
         * 使用 behavior 的方式挂载methods
         */
        this.__unmnt__ = (e) => {
          const widget = getWidget($page.widgets, e.target.id);
          widget._methods = {};
        };
        this._disposers = this.initMergeRenderer($page.widgets);
      },
      detached() {
        const $page = this._getInstance();
        this._pageActive = false;
        disposeWidget($page._rootWidget);
        this._disposers.forEach((dispose) => dispose());
      },
    },
    pageLifetimes: {
      // 组件所在页面的生命周期函数，定义下给运营平台上报用
      show: function () {},
      hide: function () {},
    },
    methods: {
      _pageActive: true,
      /** page lifecycles **/
      ...extractLifecycles(),
      ...evtHandlers,
      ...mergeRenderer,
      onLoad(options) {
        const $page = this._getInstance();
        setConfig({ currentPageId: $page.uuid });
        app.__internal__.activePage = $page;
        this._pageActive = true;

        let query = decodePageQuery(options || {});
        EXTRA_API.setParams($page.uuid, query);
        createStateDataSourceVar($page.uuid, generateParamsParser({ app, $page }));

        const hook = lifecycle.onLoad || lifecycle.onPageLoad;
        hook?.call?.($page, query);

        this.invokeEventHandler(id, 'load', { detail: { query } });
      },
      onReady() {
        const $page = this._getInstance();

        this._disposers.push(...runWatchers(index, this));

        const hook = lifecycle.onReady || lifecycle.onPageReady;
        hook?.call?.($page);

        this.invokeEventHandler(id, 'ready');
      },
      onUnload() {
        const $page = this._getInstance();

        const hook = lifecycle.onUnload || lifecycle.onPageUnload;
        hook?.call?.($page);

        this.invokeEventHandler(id, 'unload');
      },
      async onShow() {
        const $page = this._getInstance();
        setConfig({ currentPageId: $page.uuid });
        app.__internal__.activePage = $page;
        this._pageActive = true;

        // 权限检查
        if (await checkAuth(app, app.id, $page)) {
          this.setData({
            weDaHasLogin: true,
          });
          const hook = lifecycle.onShow || lifecycle.onPageShow;
          hook?.call?.($page);
          this.invokeEventHandler(id, 'show');
        }
      },
      onHide() {
        const $page = this._getInstance();
        const hook = lifecycle.onHide || lifecycle.onPageHide;
        hook?.call?.($page);
        this._pageActive = false;

        // 触发页面节点事件
        this.invokeEventHandler(id, 'hide');
      },
      invokeEventHandler(id, triggerName, params = {}) {
        const keyName = getMpEventHandlerName(id, triggerName);
        typeof evtHandlers?.[keyName] === 'function' && evtHandlers[keyName].call(this, params);
      },
      _getInstance() {
        let $page = this.$WEAPPS_PAGE;
        if ($page) {
          return $page;
        }
        Object.assign(pageContext, {
          id,
          uuid,
          state: observable(pageState),
          widgets: {},
        });
        $page = pageContext;
        this.$WEAPPS_PAGE = $page;
        $page.handler = Object.keys(handler).reduce((result, key) => {
          result[key] = handler[key].bind($page);
          return result;
        }, {});
        $page.computed = createComputed(pageComputed, $page);
        let dataset = createDataset($page.uuid);
        $page.dataset = dataset;
        $page.state.dataset = dataset;
        $page.setState = (userSetState) => {
          Object.keys(userSetState).forEach((keyPath) => {
            app.utils.set($page.dataset.state, keyPath, userSetState[keyPath]);
          });
        };

        const { widgets, rootWidget } = createWidgets(widgetProps, dataBinds, this,  $page.widgets);
        $page.widgets = widgets
        $page._rootWidget = rootWidget;

        $page.invokeComponentMethod = ({ component, method, params }) => {
          const components = $page.widgets[component];
          let componentInstance = undefined;
          if (Array.isArray(components)) {
            if (components.length > 1) {
              throw new Error(`调用方法失败：id为${component}的组件拥有多个实例`);
            }
            componentInstance = components[0];
          } else {
            componentInstance = components;
          }

          const currentInstanceRef = componentInstance?._getInstanceRef()?.current || {};
          const { methods = {}, ...restInstanceRef } = currentInstanceRef;

          const readonlyMap = {
            ...componentInstance?._methods,
            ...restInstanceRef,
            ...methods,
          };

          if (typeof readonlyMap[method] !== 'function') {
            throw new Error(`调用方法失败：未找到id为${component}下的方法${method}`);
          }
          return readonlyMap[method](params);
        };

        return $page;
      },
    },
  });
}

function decodePageQuery(query) {
  return Object.keys(query).reduce((decoded, key) => {
    decoded[key] = decodeURIComponent(query[key]);
    return decoded;
  }, {});
}
