import * as vue from 'vue';

/** 该文件以Vue3版本的适配器为依据，编写了本声明，本文涉及以下术语：
 * instance     :  即一个Vue3组件实例， conext, attrs，emit等很多属性
 * vm，parentVm :  适配器封装的一个数据结构，具有大量$属性，以及组件上其它属性
 * emitter      :  适配器中，自己封装的一个EventBus对象，和instance.emit没关系。 vm.$emit 会同时触发 instance.emit和instance.$emitter。
 * subTree      :  instance.subTree 是组件当前的vnode描述
 */

/** hooks 变量: renderless的第二个参数 */
type ISharedRenderlessParamHooks = typeof vue;
/** utils变量: renderless的第三个参数  */
interface ISharedRenderlessParamUtils<CT = never> {
    /** 当前使用的框架 */
    framework: 'vue3' | 'vue2' | 'vue2.7';
    /** 组件前缀，默认为 Tiny  */
    $prefix: string;
    /** 所有规范的配置 */
    globalDesignConfig: {
        components?: IComponentDesignConfig;
        name?: string;
        version?: string;
    };
    /** 当前组件的规范配置 */
    designConfig: IComponentDesignConfig;
    /** app的root上的i18n对象，renderless基本没有使用它 */
    i18n: any;
    /** 组件内国际化，常用函数 */
    t: (path: string, options?: any) => string;
    /** 组件模式。
     * @mark 等同于 vm.$mode, 取值为：pc  mobile  mobile-first */
    mode: 'pc' | 'mobile' | 'mobile-first';
    /** 是否为 mobile */
    isMobileMode: boolean;
    /** 是否为 pc */
    isPCMode: boolean;
    /** 是否为mobile-first */
    isMobileFirstMode: boolean;
    /** vue组件实例的context.attrs */
    attrs: any;
    /** 返回组件props._constants */
    constants: CT;
    /** vue的内置nextTick， 其实可以从第二参 hooks中获取, 还可以通过当前vm.$nextTick来获取 */
    nextTick: typeof vue.nextTick;
    /** parentVm */
    parent: ITinyVm<CT>;
    /** 当前vm */
    vm: ITinyVm<CT>;
    /** 第一次mount加载后的所有refs，不会更新！ ⭐要避免使用 */
    refs: Record<string, any>;
    /** 组件初化时的页面路由，不会更新！
     * @mark ⭐要避免使用, 请使用vm.$refs */
    route: any;
    /** 当前的router实例。 从app的根上读取 */
    router: any;
    /** 从app的根上读取: root?.$service,  用户必须引用服务适配器的包， 并传入app后，才有该变量 */
    service: any;
    /** 调用从app的根上$getService:  root?.$getService(vm) */
    getService: any;
    /** 返回 instance.slots。 vue2,vue3的这2个属性是同一个引用： scopedSlots===slots。  */
    slots: Record<string, Function>;
    /** 返回 instance.slots */
    scopedSlots: Record<string, Function>;
    /** 向实例的 subTree.children 深度遍历发送广播消息。 如果component找到了触发component.emit(eventName, params)
     * @param componentName 组件名
     */
    broadcast: (componentName: string, eventName: string, params?: any) => void;
    /** 向实例的 parent开始查找componentName， 找到匹配第一个组件， 触发component.emit(eventName, params) */
    dispatch: (componentName: string, eventName: string, params?: any) => void;
    /** 向实例的subTree 遍历。
     * 1、handler 是函数： handler入参{level,vm,el,options,isLevel1}. 给每个子节点遍历调用函数。
     * 2、handler 是非函数： 直接通过 instance.subTree 来生成一个同等树状的vm数据。
     * @mark ⭐全局有4个组件使用到`handler是函数`的用法 ，用于遍历子节点，将符合条件的信息收集到一个数组中。
     */
    childrenHandler: (handler?: (node: {
        level: number;
        vm: ITinyVm<CT>;
        el: any;
        options: any;
        isLevel1: any;
    }) => void) => void | any[];
    /** 向实例的 parent 遍历。
     * 1、handler 是函数： handler入参{level,vm,el,options}. 遍历父节点。若handler处理某个节点时，返回true，则停止！
     * 2、handler 是非函数： 直接返回{level,vm,el,options} 的数据体。
     * @mark ⭐全局未有使用该函数
     */
    parentHandler: (handler?: () => boolean) => void | any;
    /** 给 instance.parent 的ctx 赋值,同时给parentVm 赋值。 适配器的内部方法⭐要避免使用 */
    setParentAttribute: ({ name, value }: {
        name: any;
        value: any;
    }) => void;
    /** 给 vm 和 instance.ctx 同时添加属性。
     * @param props Object.defineProperties的第2参, 格式：{ property1: {value: 42,writable: true},property2: {...} }
     */
    defineInstanceProperties: (props: PropertyDescriptorMap & ThisType<any>) => void;
    /** 给parentVm 添加属性。 */
    defineParentInstanceProperties: (props: PropertyDescriptorMap & ThisType<any>) => void;
    /** 返回 context.emit */
    emit: (event: any, ...args: any[]) => void;
    /** Tiny 适配器中，自行封装的一个 EventBus模型的工厂函数。⭐每次调用才返回一个新的模型对象。
     * @mark vm中，会给instance添加一个$emitter=emitter() ,就是该函数返回的模型。 vm中的$on, $off,$once 都是该模型的方法。
     * @mark ⭐所以不要直接使用该函数。
     */
    emitter: () => {
        emit: (eventname: string) => void;
        on: (eventname: string, callback: Function, once?: boolean) => void;
        off: (eventname: string, callback: Function) => void;
        once: (eventname: string, callback: Function) => void;
    };
    /** 合并 tailwind 类 */
    mergeClass: (...cssClasses: any[]) => string;
}
/** vue.ts的一个混合上下文，也是index.ts 文件中二层函数的入参混合体。 */
type ISharedRenderlessFunctionParams<CT = null> = {
    api: object;
    props: object;
    state: object;
} & ISharedRenderlessParamHooks & ISharedRenderlessParamUtils<CT>;
/** 单个组件的规范的配置 */
interface IComponentDesignConfig {
    icons?: Record<string, any>;
    renderless?: (props: any, hooks: ISharedRenderlessParamHooks, utils: ISharedRenderlessParamUtils<any>, sdk: any) => any;
    [otherKey: string]: any;
}
/** Tiny-Vue的Adapter 内部为组件创建的Vm */
interface ITinyVm<CT = never> {
    /** 从instance.attrs上， 过滤出所有的非函数的attrs */
    $attrs: Record<string, any>;
    /** 从instance.attrs上， 过滤出所有的函数的attrs, 并对大驼峰属性名转化为下划线名 */
    $listeners: Record<string, Function>;
    /** 返回instance.subTree的同等树状的vm数据 */
    $children: any[];
    /** 返回 instance.props._constants */
    $constants: CT;
    /** 返回 instance.vnode.el */
    $el: HTMLElement;
    /** 返回 instance._tiny_mode */
    $mode: 'pc' | 'mobile' | 'mobile-first';
    /** 即 hooks.nextTick */
    $nextTick: typeof vue.nextTick;
    /** 自动触发 instance.emit 和 instance.$emitter 两处地方 */
    $emit: (...args: any[]) => void;
    /** instance.$emitter.off, 不传任何参数则是移除所有事件 */
    $off: (eventname?: string, callback?: Function) => void;
    /** instance.$emitter.on */
    $on: (eventname: string, callback: Function) => void;
    /** instance.$emitter.once */
    $once: (eventname: string, callback: Function) => void;
    /** 返回 instance.type.componentName。
     * @mark instance.type 可能是字符串，也可能是组件类，所以只有组件类的时候，才能返回组件的真实名字 */
    $options: {
        componentName: string | undefined;
    };
    /** 返回 instance.parent  */
    $parent: vue.ComponentPublicInstance;
    /** 返回 instance.refs,
     * @mark ⭐ 尽量使用这个，每次都会是最新的refs */
    $refs: Record<string, any>;
    /** 返回 instance.slots */
    $slots: Record<string, Function>;
    /** 返回 instance.slots */
    $scopedSlots: Record<string, Function>;
    /** 给target赋值。 vue2是调用instance.$set
     * @mark vue3是响应数据，直接转为 target[propertyName] = value。
     * @mark vue2 可能有数据修改不触发响应，就要调用$set去修改值。
     * @whenUse ⭐ Vue2组件中修改数据不触发响应时，要使用该$set. instance.$set
     */
    $set: (target: any, propertyName: any, value: any) => void;
    /** 返回 props.tiny_renderless */
    $renderless: Function;
    /** 返回 props.tiny_template */
    $template: any;
    /** 适配器或组件内，可能会赋值很多其它属性 */
    [otherProps: string]: any;
}

export { ISharedRenderlessFunctionParams, ISharedRenderlessParamHooks, ISharedRenderlessParamUtils, ITinyVm };
