import { v4 } from 'uuid'

/**
 * 定时任务
 */
export const timerTask = new (class {
    private timeoutMap = new Map<string, number>();
    private intervalMap = new Map<string, number>();

    /**
     * 设置定时器
     * @param callback 
     * @param delay 
     * @param timerId 
     */
    setTimeout(callback: () => void, delay = 100, timerId?: string) {
        const uuid = timerId || v4();
        this.clearTimeout(uuid);

        const timer = window.setTimeout(() => {
            this.timeoutMap.delete(uuid);
            callback();
        }, delay)

        this.timeoutMap.set(uuid, timer);

        return {
            uuid,
            clear: () => this.clearTimeout(uuid)
        }
    }

    /**
     * 设置周期定时器
     * @param callback 
     * @param delay 
     * @param timerId 
     */
    setInterval(callback: () => void, delay = 100, timerId?: string) {
        const uuid = timerId || v4();
        this.clearInterval(uuid);

        const timer = window.setInterval(() => {
            callback();
        }, delay)

        this.intervalMap.set(uuid, timer);

        return {
            uuid,
            clear: () => this.clearInterval(uuid)
        }
    }

    /**
     * 清除定时器
     * @param keys 
     */
    clearTimeout(...keys: string[]) {
        if (keys.length) {
            keys.forEach((key) => {
                if (this.timeoutMap.has(key)) {
                    window.clearTimeout(this.timeoutMap.get(key));
                    this.timeoutMap.delete(key);
                }
            })
        } else {
            for (const [key, value] of this.timeoutMap.entries()) {
                window.clearTimeout(value);
                this.timeoutMap.delete(key);
            }
        }
    }

    /**
     * 清除周期定时器
     * @param keys 
     */
    clearInterval(...keys: string[]) {
        if (keys.length) {
            keys.forEach((key) => {
                if (this.intervalMap.has(key)) {
                    window.clearInterval(this.intervalMap.get(key));
                    this.intervalMap.delete(key);
                }
            })
        } else {
            for (const [key, value] of this.intervalMap.entries()) {
                window.clearInterval(value);
                this.intervalMap.delete(key);
            }
        }
    }

    /**
     * 清除所有定时器
     */
    clearAll() {
        this.clearTimeout();
        this.clearInterval();
    }
})

/**
 * 定时拦截器
 */
export const timerInterceptor = new (class {
    private debounceMap = new Map<string, number>();
    private throttleMap = new Map<string, () => void>();

    /**
     * 函数防抖(手动触发)
     * @param callback 回调函数
     * @param delay 延迟毫秒数，默认100毫秒
     * @returns 
     */
    setDebounce<T extends unknown[]>(callback: (...params: T) => void, delay = 100) {
        let timer = 0;
        return function (...args: T) {
            clearTimeout(timer);
            timer = window.setTimeout(() => {
                callback(...args);
            }, delay)
        }
    }

    /**
     * 函数节流(手动触发)
     * @param callback 回调函数
     * @param delay 延迟毫秒数，默认100毫秒
     * @returns 
     */
    setThrottle<T extends unknown[]>(callback: (...params: T) => void, delay = 100) {
        let timer = 0;
        return function (...args: T) {
            if (timer === 0) {
                timer = window.setTimeout(() => {
                    callback(...args);
                    timer = 0;
                }, delay)
            }
        }
    }

    /**
     * 函数防抖(立即触发)
     * @param callback 回调函数
     * @param delay 延迟毫秒数，默认100毫秒
     * @param timerId 计时器ID
     */
    debounce(callback: () => void, delay = 100, timerId = 'timer') {
        if (this.debounceMap.has(timerId)) {
            const t = this.debounceMap.get(timerId);
            window.clearTimeout(t);
        }

        const timer = window.setTimeout(() => {
            callback();
            this.debounceMap.delete(timerId);
        }, delay)

        this.debounceMap.set(timerId, timer);
    }

    /**
     * 函数节流(立即触发)
     * @param callback 回调函数
     * @param delay 延迟毫秒数，默认100毫秒
     * @param timerId 计时器ID
     * @returns 
     */
    throttle(callback: () => void, delay = 100, timerId = 'timer') {
        if (this.throttleMap.has(timerId)) {
            this.throttleMap.set(timerId, callback); // 更新最新函数
            return;
        }

        window.setTimeout(() => {
            const fn = this.throttleMap.get(timerId);
            fn && fn();
            this.throttleMap.delete(timerId);
        }, delay)

        this.throttleMap.set(timerId, callback);
    }
})