export default class {
    private ws: WebSocket | null = null; // WebSocket 对象
    private connectionId = 0;
    private url = '';
    private protocols?: string | string[];
    private isReconnecting = false; // 是否正在重连
    private messageTimer = 0; // 消息超时定时器
    private messageTimeout = 1000 * 15; // 消息超时时间
    private heartbeatTimer = 0; // 心跳定时器
    private heartbeatInterval = 1000 * 30; // 心跳间隔时间
    private reconnectTimer = 0; // 重连定时器
    private reconnectCount = 0; // 本次已重连次数
    private reconnectLimit = 10; // 限制重连次数，0 = 无限制

    private heartbeatMessage?: () => string | ArrayBufferLike | Blob | ArrayBufferView; // 心跳消息
    private onOpen?: () => void; // 连接成功的回调
    private onMessage: (data: unknown) => void; // 消息回调
    private onClose?: () => void; // 连接断开的回调
    private onError?: (err: Event) => void; // 连接发生错误的回调
    private onBeforeReconnect?: (count: number) => void; // 重连之前的回调
    private onReconnect?: () => void; // 重连成功之后的回调

    constructor(options: {
        heartbeatMessage?: () => string | ArrayBufferLike | Blob | ArrayBufferView;
        onOpen?: () => void;
        onMessage: (data: unknown) => void;
        onClose?: () => void;
        onError?: (err: Event) => void;
        onBeforeReconnect?: (count: number) => void;
        onReconnect?: () => void;
    }) {
        this.heartbeatMessage = options.heartbeatMessage
        this.onOpen = options.onOpen
        this.onMessage = options.onMessage
        this.onClose = options.onClose
        this.onError = options.onError
        this.onBeforeReconnect = options.onBeforeReconnect
        this.onReconnect = options.onReconnect
    }

    // 创建 WebSocket 连接
    connection(url: string, protocols?: string | string[]) {
        clearTimeout(this.reconnectTimer)
        this.stopHeartbeat()

        const currentConnectionId = this.connectionId + 1
        this.connectionId = currentConnectionId
        this.url = url
        this.protocols = protocols

        this.ws?.close()
        this.ws = this.protocols ? new WebSocket(this.url, this.protocols) : new WebSocket(this.url)
        console.log(this.url, '正在连接')

        // 连接成功
        this.ws.onopen = () => {
            if (this.connectionId === currentConnectionId) {
                if (this.reconnectCount) {
                    this.onReconnect?.()
                } else {
                    this.onOpen?.()
                }

                console.log(this.url, '连接成功')
                this.reconnectCount = 0
                this.startHeartbeat()
            } else {
                this.ws?.close()
            }
        }

        // 接收消息
        this.ws.onmessage = (event) => {
            this.stopHeartbeat()

            if (this.connectionId === currentConnectionId) {
                this.startHeartbeat()
                this.onMessage?.(event.data)
            }
        }

        // 连接断开
        this.ws.onclose = () => {
            this.stopHeartbeat()
            this.ws = null
            this.isReconnecting = false

            // 判断是否当前实例，重连时不处理旧 WebSocket 事件
            if (this.connectionId === currentConnectionId) {
                if (this.reconnectCount) {
                    console.warn(this.url, `第${this.reconnectCount}次重连失败`)
                } else {
                    console.warn(this.url, '连接已断开')
                }
                this.reconnect() // 重连失败会不断尝试，直到成功为止
            }
        }

        // 连接发生错误
        this.ws.onerror = (error) => {
            if (this.connectionId === currentConnectionId) {
                console.error(this.url, '连接发生错误')
                this.onError?.(error)
            }
        }
    }

    // 主动断开连接，断开后不会自动重连
    disconnect(forced = false) {
        return new Promise<void>((resolve) => {
            clearTimeout(this.reconnectTimer)
            this.reconnectCount = 0
            this.connectionId++

            if (!forced && this.ws) {
                const listener = () => {
                    console.warn(this.url, '主动断开')
                    this.ws?.removeEventListener('close', listener)
                    this.onClose?.()
                    resolve()
                }
                this.ws.addEventListener('close', listener)
                this.ws.close()
            } else {
                if (this.ws) {
                    console.warn(this.url, '主动断开')
                    this.ws.close()
                }
                this.onClose?.()
                resolve()
            }
        })
    }

    // 发送消息
    send(data: string | ArrayBufferLike | Blob | ArrayBufferView) {
        this.ws?.send(data)
    }

    // 断开重连
    private reconnect() {
        if (!this.isReconnecting && this.reconnectCount < this.reconnectLimit) {
            this.isReconnecting = true
            this.reconnectCount++
            this.onBeforeReconnect?.(this.reconnectCount)

            // 自动计算每次重试的延时，重试次数越多，延时越大
            const delay = this.reconnectCount * 3000
            console.log(this.url, `${delay / 1000}秒后将进行第${this.reconnectCount}次重连`)

            this.reconnectTimer = setTimeout(() => {
                this.connection(this.url, this.protocols)
            }, delay)
        }
    }

    // 发送心跳检测
    private startHeartbeat() {
        const message = this.heartbeatMessage?.()
        if (message) {
            this.heartbeatTimer = setTimeout(() => {
                this.send(message)
                // 如果已经超过或心跳超时时长没有收到心跳回复，则认为网络已经异常，进行断网重连
                this.messageTimer = setTimeout(() => {
                    console.warn(this.url, '心跳超时')
                    this.reconnect()
                }, this.messageTimeout)
            }, this.heartbeatInterval)
        }
    }

    // 停止心跳检测
    private stopHeartbeat() {
        clearTimeout(this.messageTimer)
        clearTimeout(this.heartbeatTimer)
    }
}