interface SocketOptions {
	heartbeatInterval?: number;
	reconnectInterval?: number;
	maxReconnectAttempts?: number;
	isReconnect?: boolean;
	uid?: string;
	subscribe?: string;
}

class Socket {
	url: string;
	ws: WebSocket | null = null;
	opts: SocketOptions;
	reconnectAttempts: number = 0;
	listeners: { [key: string]: Function[] } = {};
	heartbeatInterval: number | null = null;

	constructor(url: string, opts: SocketOptions = {}) {
		this.url = url;
		this.opts = {
			heartbeatInterval: 2 * 1000, // 心跳间隔
			reconnectInterval: 2 * 1000, // 重连间隔
			maxReconnectAttempts: 10, // 最大重连次数
			isReconnect: true, // 是否需要重连
			uid: '', // 用户id
			subscribe: '', // 订阅的频道
			...opts,
		};

		this.init();
	}

	init() {
		this.ws = new WebSocket(this.url);

		this.ws.onopen = this.onOpen.bind(this);
		this.ws.onmessage = this.onMessage.bind(this);
		this.ws.onerror = this.onError.bind(this);
		this.ws.onclose = this.onClose.bind(this);
	}

	onOpen(event: Event) {
		// this.reconnectAttempts=0;
		this.startSubscribe();
		this.startHeartbeat();
		this.emit('open', event);
	}

	onMessage(event: MessageEvent) {
		this.emit('message', event);
	}

	onError(event: Event) {
		console.error('WebSocket error:', event);
		this.emit('error', event);
	}

	onClose(event: CloseEvent) {
		this.stopHeartbeat();
		this.emit('close', event);

		// @ts-ignore
		if (this.opts.isReconnect) {
			this.reConnect();
		}
	}

	reConnect() {
		// @ts-ignore
		if (this.reconnectAttempts < this.opts.maxReconnectAttempts) {
			setTimeout(() => {
				this.reconnectAttempts++;
				this.init();
			}, this.opts.reconnectInterval);
		} else {
			console.error('已到达重连次数最高，请手动刷新重连');
		}
	}

	startHeartbeat() {
		if (!this.opts.heartbeatInterval) return;
		this.heartbeatInterval = window.setInterval(() => {
			if (this.ws?.readyState === WebSocket.OPEN) {
				this.send({
					id: '',
					type: 2,
					from: this.opts.uid,
					to: 'sys',
					timestamps: new Date().getTime(),
					body: 'PING',
				});
			}
		}, this.opts.heartbeatInterval);
	}

	startSubscribe() {
		// @ts-ignore
		const moreTime = this.opts.heartbeatInterval + 100;
		setTimeout(() => {
			if (!this.opts.subscribe) return;
			this.send({
				id: '',
				type: 8,
				from: this.opts.uid,
				to: this.opts.subscribe,
				timestamps: new Date().getTime(),
				body: 'subscribe',
			});
		}, moreTime);
	}
	stopHeartbeat() {
		if (this.heartbeatInterval) {
			clearInterval(this.heartbeatInterval);
			this.heartbeatInterval = null;
		}
	}

	send(data: any) {
		if (this.ws?.readyState === WebSocket.OPEN) {
			this.ws.send(JSON.stringify(data));
		} else {
			console.error('WebSocket is not open. Cannot send:', data);
		}
	}

	on(event: string, callback: Function) {
		if (!this.listeners[event]) {
			this.listeners[event] = [];
		}
		this.listeners[event].push(callback);
	}

	off(event: string) {
		if (this.listeners[event]) {
			delete this.listeners[event];
		}
	}

	emit(event: string, data: any) {
		this.listeners[event]?.forEach((callback) => callback(data));
	}

	close() {
		this.opts.isReconnect = false;
		this.ws?.close();
		this.ws = null;
		this.stopHeartbeat();
		this.listeners = {};
	}
}

export function useSocket(url: string, opts?: SocketOptions) {
	const socket = new Socket(url, opts);
	return {
		socket,
		close: socket.close.bind(socket),
		send: socket.send.bind(socket),
		on: socket.on.bind(socket),
		off: socket.off.bind(socket),
	};
}
