import ReconnectingWebSocket, {type Options, type Event} from 'reconnecting-websocket';
import mitt from "mitt"
import {uuid} from "~/utils/index";

export namespace XYWebSocket {
    export class WebSocketClass {
        public socket: ReconnectingWebSocket;
        public socketUrl: string;
        public options: SocketOptions = {
            apiTimeout: 10, // 接口请求超时 s
            connectionTimeout: 10 * 1000, // 超时连接 ms
            maxRetries: 5, // 最大重连次数
        };
        private eventPoll: Map<string, SocketResponseType> = new Map();
        public SocketStatus: SocketStatusType = SocketStatusType.close;

        constructor(url: string, options?: SocketOptions) {
            this.socketUrl = url;
            this.options = {
                ...this.options,
                ...(options || {})
            };
            this.socket = new ReconnectingWebSocket(this.socketUrl, [], this.options);
            this.initWebSocket();
        }

        public initWebSocket() {
            console.info(this.socketUrl, '正在连接websocket........');
            this.socket.addEventListener('open', this.websocketOnopen.bind(this));
            this.socket.addEventListener('message', this.websocketOnmessage.bind(this));
            this.socket.addEventListener('error', this.websocketOnerror.bind(this));
            this.socket.addEventListener('close', this.websocketOnclose.bind(this));
            return this.socket;
        }

        private websocketOnopen(e: Event) {
            console.info('WebSocket连接成功', e);
            this.changeSocketStatus(SocketStatusType.open)
        }

        // 收到服务端消息
        private websocketOnmessage(e: MessageEvent) {
            try {
                if (!e) return;
                if (!e.data) return;
                const messagePackage = JSON.parse(e.data) || {};
                console.log('---------接收---------', messagePackage, '-----------消息包------');
                const {web_request_id = null} = messagePackage.map || {};

                SocketEventsBus.emit(SocketEvents.chatEvent, messagePackage)

                // 处理自己本地发送的消息
                if (web_request_id && this.eventPoll.has(web_request_id)) {
                    const eventItem = this.eventPoll.get(web_request_id)!;
                    eventItem.response = messagePackage;
                    eventItem.status = 'end'; // todo status属性的修改最好写在最后，写在前面好像也没啥问题
                }
            } catch (e) {

            }
        }

        private websocketOnerror(e: Event) {
            console.info(this.socket, 'WebSocket连接出错', e);
            this.changeSocketStatus(SocketStatusType.close)
        }

        private websocketOnclose(e: Event) {
            console.info(this.socket, 'WebSocket连接关闭', e);
            this.changeSocketStatus(SocketStatusType.close)
        }

        private changeSocketStatus(status: SocketStatusType) {
            this.SocketStatus = status;
            SocketEventsBus.emit(SocketEvents.socketStatusChange, this.SocketStatus)
        }

        public destroyWebsocket() {
            this.socket.removeEventListener('open', this.websocketOnopen.bind(this));
            this.socket.removeEventListener('message', this.websocketOnmessage.bind(this));
            this.socket.removeEventListener('error', this.websocketOnerror.bind(this));
            this.socket.removeEventListener('close', this.websocketOnclose.bind(this));
        }

        public async sendMessage(data?: any): Promise<any> { // string | ArrayBuffer | Blob | ArrayBufferView
            const requestId = uuid();

            let timer: any = 0;
            return new Promise((resolve, reject) => {
                // 整理好请求，记录到本地请求池
                const eventItem: SocketResponseType = {
                    requestId: requestId,
                    status: 'waiting',
                    response: null,
                };
                const eventItemProxy = new Proxy(eventItem, {
                    set(target: SocketResponseType, p: keyof SocketResponseType, value, receiver): boolean {
                        // @ts-ignore
                        target[p] = value;
                        if (target.status === 'end') {
                            // console.warn('------Proxy 检测到状态变化 end---------')
                            resolve(target.response);
                        }
                        return true;
                    }
                });
                this.eventPoll.set(requestId, eventItemProxy);

                const request = {
                    ...data,
                    map: {
                        web_request_id: requestId,
                    }
                };
                console.warn('---------发送---------', request, '-----------消息包------');
                this.socket.send(JSON.stringify(request));

                // 判断超时
                let loop = 0;
                timer = setInterval(() => {
                    if (this.eventPoll.has(requestId)) {
                        const item = this.eventPoll.get(requestId)!;
                        if (item.status === 'end') {
                            clearInterval(timer);
                        } else {
                            loop++;
                            if (loop > this.options.apiTimeout!) {
                                clearInterval(timer);
                                reject('请求超时' + 'requestId:' + requestId)
                            }
                        }
                    }
                }, 1000)
            })
                .finally(() => {
                    this.eventPoll.delete(requestId);
                });
        }
    }

    export enum SocketStatusType {
        close,
        open,
        error,
    }

    export const SocketEventsBus = mitt();

    interface SocketResponseType {
        requestId: string;
        status: 'waiting' | 'end';
        response: any
    }

    interface SocketOptions extends Options {
        apiTimeout?: number
    }

    export const SocketEvents = {
        chatEvent: 'chat-event',
        socketStatusChange: 'socket-status-change'
    }
}
