/**
 * @namespace 任务调度器
 * @description 负责将耗时任务拆分到多个线程中执行
 * 重新设计，基于任务调度的方式 使用多线程 这个可以单独设计多个模块（任务调度和任务切分模块， 真的有DOM 在线程中使用模拟代理的模式）
 * A 主线程负责通信，调度和消费消息
 * B 线程 负责解析实体
 * C 线程 负责解析实体
 * B C 线程 解析不重复
 */

// import { BSON } from 'bson'
// @ts-ignore
// export const IsWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope
// export const HasSharedBuffer = globalThis.SharedArrayBuffer != null
export const ProxyMarker = Symbol.for("TaskProxy.proxy");
export const ArgsTransfer = Symbol.for("TaskProxy.args.transfer");
export const ReturnTransfer = Symbol.for("TaskProxy.return.transfer");
export const NowaitMarker = Symbol.for("TaskProxy.nowait");
export const ThrowMarker = Symbol.for("TaskProxy.thrown");

export const enum MessageType {
    GET = 1,
    SET = 2,
    APPLY = 3,
    CONSTRUCT = 4,
    ENDPOINT = 5,
    RELEASE = 6,
    INIT = 7,
}
export const enum WireValueType {
    RAW = 7,
    PROXY = 8,
    THROW = 9,
    HANDLER = 10,
}
export type Message = {
    id?: string | number;
    op: MessageType;
    path?: string[];
    args?: WireValue[];
    value?: WireValue[];
    scope?: any;
}
export type PathKey = (string | number | symbol);
export type MsgParams = { type: string, path?: PathKey[], args?: any[], [key: string]: any };
export type WireValue = {
    id?: string | number;
    return?: boolean;
    isError?: boolean;
    scope?: any;
    type: WireValueType;
    value: unknown;
}

/**
 * 
 * @param value 
 * @param argsTransfer 表示该函数被操作时，处理参数和值
 * @param returnTransfer 表示该函数被操作时，处理参数和值
 * @param nowait 
 * @returns 
 */
export function proxy(value: any, argsTransfer?: string | null, returnTransfer?: string | null, nowait?: boolean) {
    return Object.assign(value, {
        [ProxyMarker]: true,
        [ArgsTransfer]: argsTransfer ? argsTransfer : null,
        [ReturnTransfer]: returnTransfer ? returnTransfer : null,
        [NowaitMarker]: !!nowait
    })
}


export class TaskWorker {
    id: number;
    threads: number;
    worker: globalThis.Worker;
    waitMap = new Map<string | number, [Function, Function]>();
    proxyRegistry = new Map<any, any>()
    proxyCache: any = {};
    isAutoProxy: boolean = true;
    // isSync: boolean;
    RunInstance?: any;
    _initing: Promise<any> | null = null;
    _finalizers: any;
    _UID: number = 0;
    [key: `${string}Transfer`]: Function;
    constructor(worker: Worker, id: number, threads = 1) {
        this.id = id;
        this.threads = threads;
        this.worker = worker;
        // this.isSync = isSync;
        this.worker.addEventListener('message', this.msgHandler);
        this._finalizers = "FinalizationRegistry" in globalThis ? new globalThis.FinalizationRegistry((val: any) => {
            this.postMessage({
                op: MessageType.RELEASE,
                scope: val,
            })
            this._finalizers.unregister(this.proxyRegistry.get(val));
            this.proxyRegistry.delete(val);
        }) : null as any
    }
    expose(key: string = 'default', value: object) {
        this.proxyCache[key] = value;
    }
    initial = (param?: any) => {
        this._initing = this.postMessage({
            op: MessageType.INIT,
        }, { id: this.id, threads: this.threads, param });
        return this;
    }
    terminate() {
        try {
            this.RunInstance?.close?.(true);
        } catch (error) {

        }
        this.RunInstance = null;
        return this.worker.terminate();
    }
    postMessage = async (msg: any, rawValue?: any, transfer?: string, isReturn?: boolean) => {
        if (this._initing != null) {
            await this._initing;
            this._initing = null;
        }
        msg = Object.assign(this._toWireValue(rawValue), msg)
        if (msg.args != null) {
            // 是否启动自动转换机制 function 非代理类型
            msg.args = msg.args.map((arg: any) => this._toWireValue(arg)) as any[];
        }
        const id = msg.id ??= ++this._UID;
        // @ts-ignore
        this.worker.postMessage(msg, (transfer ? this[transfer as string]?.(msg, isReturn) : undefined));
        return this._promise(id);
    }
    msgHandler = (e: MessageEvent) => {
        if (e.data.return) {
            this.returnBody(e.data);
        } else {
            this.execBody(e.data);
        }
    }
    /**
     * @description 接收来自执行函数的返回结果
     * @param data 
     * @returns 
     */
    returnBody(data: WireValue) {
        const id = data?.id;
        const promiseFn: any = id == null ? null : this.waitMap.get(id);
        if (promiseFn == null) return;
        this.waitMap.delete(id as any);
        // 接收执行返回的参数=> 解析成正常值
        Promise.resolve().then(() => {
            // 执行_fromWireValue 可能 返回或者抛出一个错误
            return this._fromWireValue(data);
        }).then(...promiseFn);
    }
    /**
     * @description 接收方执行调用消息
     * @param data 
     * @returns 
     */
    execBody(data: Message) {
        const { id, op } = data;
        const path = data.path || [];
        // 对参数接收的参数进行转换
        const args = (data.args || []).map(this._fromWireValue);
        // 先临时使用
        const target = this.proxyCache[data.scope ?? 'default'] || this;
        const last = path[path.length - 1];
        const noPath = last == null;
        let returnValue, transfer: any;
        try {
            // 如果访问不到是否执行初始化 proxyCache
            const parent = noPath ? target : path.slice(0, -1).reduce((target: any, prop: string | number) => target[prop], target);
            const rawValue = noPath ? target : parent[last];
            const nowait = rawValue?.[NowaitMarker] === true;
            if (nowait) this.postMessage({ id, return: true });
            switch (op) {
                case MessageType.APPLY:
                    {
                        returnValue = rawValue.apply(parent, args);
                        transfer = rawValue[ReturnTransfer] ?? target[ReturnTransfer];
                    }
                    break;
                case MessageType.GET:
                    {
                        returnValue = rawValue;
                        transfer = rawValue?.[ReturnTransfer] ?? target[ReturnTransfer];
                    }
                    break;
                case MessageType.SET:
                    {
                        parent[last] = this._fromWireValue(data as any);
                        returnValue = true;
                    }
                    break;

                case MessageType.CONSTRUCT:
                    {
                        returnValue = proxy(new rawValue(...args));
                    }
                    break;
                case MessageType.RELEASE:
                    {
                        // 是否需要改成 = null的形式
                        this.proxyCache[data.scope] = null;
                        returnValue = undefined;
                    }
                    break;
                case MessageType.INIT:
                    {
                        const value = this._fromWireValue(data as any)
                        this.id = value.id;
                        this.threads = value.threads;
                        returnValue = noPath ? true : (rawValue?.initial?.(value.param) || true);
                    }
                    break;
                // default:
                //     return;
            }
            if (nowait) return;
        } catch (value) {
            returnValue = value;
        }
        // Promise.resolve(returnValue).catch((value) => value).then((value) => {
        Promise.resolve(returnValue).then((value) => {
            // 返回结果端是否也需要 transfer 怎么设计
            this.postMessage({ id, return: true }, value, transfer, true);
        }).catch((error) => {
            this.postMessage({ id, return: true }, error);
        });
    }
    /**
     * 
     * @param 接收后进行检查转换 
     * @returns 
     */
    private _fromWireValue = (data: WireValue) => {
        switch (data.type) {
            case WireValueType.HANDLER:
                if (data.isError) {
                    throw Object.assign(
                        new Error(''),
                        data.value
                    );
                }
                // 接收的是个代理对象
                else {
                    /**@ts-ignore */
                    data[ProxyMarker] = true;
                    data.scope = data.value;
                    const token: any = [];
                    const tproxy: any = createTaskPorxy([this as any], [], data, true);
                    this._finalizers?.register(tproxy, data.scope, token);
                    this.proxyRegistry.set(data.scope, token);
                    return tproxy
                }
            case WireValueType.RAW:
                return data.value;
        }
    }
    /**
     * 
     * @param 发送前进行转换格式 
     * @returns 
     */
    private _toWireValue = (value: any) => {
        if (isProxyMarker(value)) {
            // 需要先缓存起来
            const scope = ++this._UID;
            this.proxyCache[scope] = value;
            return {
                type: WireValueType.HANDLER,
                transfer: value[ArgsTransfer],
                // 需要生产唯一的值 和子线程的这个值对应
                value: scope,
            }
        }
        // 自动转换
        if (this.isAutoProxy && typeof value === 'function') {
            // if (isFunction(value)) {
            // 需要先缓存起来
            const scope = ++this._UID;
            this.proxyCache[scope] = value;
            return {
                type: WireValueType.HANDLER,
                transfer: value[ArgsTransfer],
                value: scope,
            }
        }
        if (isErrorMarker(value)) {
            return {
                type: WireValueType.HANDLER,
                isError: true,
                value: {
                    message: value.message,
                    name: value.name,
                    stack: value.stack,
                },
            }
        }
        return {
            type: WireValueType.RAW,
            value,
        }
    }
    private _promise(id: string | number) {
        return new Promise((resolve, reject) => {
            this.waitMap.set(id, [resolve, reject]);
        })
    }
}

/**
 * 
 * @param threads 
 * @param path 
 * @param target 
 * @param inited 
 * @returns 
 */
export function createTaskPorxy(
    threads: (globalThis.Worker | string)[],
    path: PathKey[] = [],
    target?: any,
    inited?: boolean,
    param?: any,
    // isSync?: boolean
) {
    const Workers: any[] = inited ? threads : threads.map((worker: any, index) => {
        return new TaskWorker(typeof worker === 'string' ? new Worker(worker) : new worker(), index, threads.length).initial(param);
    });
    const before = target?.before;
    // const after = target?.after || (isSync ? defaulSynctAfter : defaultAfter);
    const after = target?.after || defaultAfter;
    const scope = target?.[ProxyMarker] && (target.scope ?? null);
    // 是否收集transfers 数据（调用端处理参数）
    const transfer = target?.transfer;
    const tproxy: any = new Proxy(function () { }, {
        get(_target, prop) {
            if (prop === '__target') {
                return target; // 返回关联的原始对象
            }
            if (before?.(MessageType.GET, path, prop)) {
                return Reflect.get(path.reduce((obj, key) => obj[key], target), prop);
            }
            if (prop === "then") {
                if (path.length === 0) {
                    return { then: () => tproxy };
                }
                const r = after(Workers, {
                    op: MessageType.GET,
                    // 需要知道那个对象执行体
                    scope,
                    path,
                })
                return r.then.bind(r);
            }
            return createTaskPorxy(Workers, [...path, prop], target, true);
        },
        set(_target, prop, rawValue) {
            if (before?.(MessageType.SET, path, prop)) {
                return Reflect.set(path.reduce((obj, key) => obj[key], target), prop, rawValue)
            }
            return after(Workers, {
                op: MessageType.SET,
                // 需要知道那个对象执行体
                scope,
                path: [...path, prop],
            }, rawValue, transfer) as any;
        },
        apply(_target, _thisArg, args) {
            const last = path[path.length - 1];
            if (last === "bind") {
                return createTaskPorxy(Workers, path.slice(0, -1), target, true);
            }
            if (last === "call" || last === "apply") {
                path = path.slice(0, -1);
                if (!isProxyMarker(args[0])) {
                    args = args.slice(1);
                }
            }
            return after(Workers, {
                // 需要知道那个对象执行体
                op: MessageType.APPLY,
                scope,
                path,
                args,
            }, undefined, transfer) as any;
        },
        construct(_target, args) {
            return after(Workers, {
                op: MessageType.CONSTRUCT,
                // 需要知道那个对象执行体
                scope,
                path,
                args,
            }, undefined, transfer) as any;
        },
    })
    // target.proxy ??= tproxy;
    return tproxy;
}


function defaultAfter(workers: TaskWorker[], params: MsgParams, rawValue?: any, transfer?: string) {
    return Promise.all(workers.map((worker) => {
        return worker.postMessage(params, rawValue, transfer)
    })).then((args: any[]) => {
        // 是否考虑合并
        return args.length > 1 ? args : args[0];
    })
}

function isObject(val: unknown): val is object {
    return (typeof val === "object" && val !== null) || typeof val === "function"
}
function isProxyMarker(val: any): boolean {
    /**@ts-ignore */
    return isObject(val) && val[ProxyMarker]
}
function isErrorMarker(val: any): boolean {
    /**@ts-ignore */
    return (isObject(val) && val[ThrowMarker]) || val instanceof Error
}
// function generateUUID(): string | number {
//     // return new Array(4).fill(0)
//     //     .map(() => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16))
//     //     .join("-");
// }

