import { IAppStatus, IAppStatusCN, MustardName } from '../typings';
import { isFunction } from '../utils/tools';

export type EventName = MustardName; // 用于区分子应用
export type BindMethod = string;

export type TCallback = (value: unknown, oldValue:unknown, source:MustardName) => void;
export type TLifeCallback = (key: MustardName) => void;
export type TDataChangeCallback = (key: MustardName, data:unknown) => void;

type EventGlobalLifeKey = `globalLife_${IAppStatusCN}`; // 全局生命周期key
type EventGlobalDataChangeKey = 'globalDataChange'; // 全局prop修改key

type EventDataKey = `data_${EventName}`; // prop注入key 
type EventDataChangeKey = `dataChange_${EventName}`; // prop修改key
type EventLifeKey = `life_${EventName}_${IAppStatusCN}`; // 生命周期key
type EventBindKey = `bind_${EventName}_${BindMethod}`; // 自定义事件key

export type EventKey = |EventDataKey
                |EventDataChangeKey
                |EventLifeKey
                |EventBindKey
                |EventGlobalLifeKey
                |EventGlobalDataChangeKey;

export interface EventValue {
    data: unknown, // 订阅的数据
    sourceOfData: MustardName, // 数据的来源
    assignment: boolean, // 是否已经赋值过
    callbacks: Set<TCallback>,
    repeatSend: WeakSet<TCallback>, // 多次dispatch相同的data,只有第一次生效, 用于去重
}

// 发布订阅系统中心
export class EventCenter {
    eventList : Map<EventKey, EventValue> = new Map();

    /**
     * 初始化
     * @param name 事件名
     * @param options 事件配置
     */
    private initEvent (name:EventKey, options:Partial<EventValue> = {}) {
        this.eventList.set(name, {
            data: undefined,
            sourceOfData: undefined,
            assignment: false,
            callbacks: new Set(),
            repeatSend: new WeakSet(),
            ...options
        });
    }

    /**
     * 订阅事件
     * @param name 事件名
     * @param fn 事件
     * @param param2.immediately 是否立即执行（对应消息dispatch过）
     * @param param2.repeatSend 多次dispatch相同的data,只有第一次生效
     */
    on (name:EventKey, fn: TCallback, { immediately, repeatSend }:{immediately?: boolean, repeatSend?: boolean} = {}) {
        if(!isFunction(fn)) {
            return;
        }
        const events = this.eventList.get(name);
        if(!events) {
            this.initEvent(name, {
                callbacks: new Set([fn]),
                repeatSend: new WeakSet(repeatSend ? [fn] : [])
            });
        }else{
            events.callbacks.add(fn);
            repeatSend && events.repeatSend.add(fn);
            immediately && events.assignment && fn(events.data, undefined, events.sourceOfData);
        }
    }

    /**
     * 注销订阅事件
     * @param name 事件名
     * @param fn 事件 不传递全部清空
     */
    off (name:EventKey, fn?: TCallback) {
        const events = this.eventList.get(name);
        if(events) {
            fn ? events.callbacks.delete(fn) : events.callbacks.clear();
        }
    }

    /**
     * 发布消息
     * @param name 事件名
     * @param source 那个应用发送的消息
     * @param data 数据
     */
    dispatch (name:EventKey, source: MustardName, data?:unknown) {
        const events = this.eventList.get(name);
        const isSameData = events?.data === data;
        if(events) {
            const oldData = events.data;
            events.assignment = true;
            for (const callback of events.callbacks) {
                // 存在且值等于上一次不执行，其他的都执行
                if(!(events.repeatSend.has(callback) && isSameData)) {
                    callback(data, oldData, source); 
                }
            }
            events.data = data;
            events.assignment = true;
            events.sourceOfData = source;
        }else{
            this.initEvent(name, {
                data: data,
                sourceOfData: source,
                assignment: true,
                callbacks: new Set([])
            });
        }
    }
}

export function getEventGlobalLifeKeyByValue (value: IAppStatusCN):EventGlobalLifeKey {
    return `globalLife_${value}`; 
}
export function getEventGlobalDataChangeKey ():EventGlobalDataChangeKey {
    return 'globalDataChange'; 
}
export function getEventDataKey (name: EventName):EventDataKey {
    return `data_${name}`;
}
export function getEventDataChangeKey (name: EventName):EventDataChangeKey {
    return `dataChange_${name}`;
}
export function getEventLifeKeyByKey (name: EventName, key: IAppStatusCN):EventLifeKey {
    return `life_${name}_${key}`; 
}
export function getEventLifeKeyByValue (name: EventName, value: IAppStatus):EventLifeKey {
    return `life_${name}_${IAppStatus[value]}` as EventLifeKey; 
}
export function getEventBindKey (name: EventName, method: BindMethod):EventBindKey {
    return `bind_${name}_${method}`; 
}