/**
 * WebSocket Client Helper
 *
 * Provides a simple interface for WebSocket connections
 */

class WebSocketClient {
	constructor(url, options = {}) {
		this.url = url
		this.ws = null
		this.reconnectAttempts = 0
		this.maxReconnectAttempts = options.maxReconnectAttempts || 5
		this.reconnectInterval = options.reconnectInterval || 3000
		this.reconnectDelay = options.reconnectDelay || 1000
		this.heartbeatInterval = options.heartbeatInterval || 30000
		this.heartbeatTimer = null
		this.isManualClose = false
		this.isConnecting = false

		// Event handlers
		this.onOpen = options.onOpen || null
		this.onMessage = options.onMessage || null
		this.onError = options.onError || null
		this.onClose = options.onClose || null

		// Message handlers
		this.messageHandlers = new Map()
	}

	/**
	 * Get connection state description
	 */
	getConnectionState() {
		if (!this.ws) return 'CLOSED'
		switch (this.ws.readyState) {
			case WebSocket.CONNECTING:
				return 'CONNECTING'
			case WebSocket.OPEN:
				return 'OPEN'
			case WebSocket.CLOSING:
				return 'CLOSING'
			case WebSocket.CLOSED:
				return 'CLOSED'
			default:
				return 'UNKNOWN'
		}
	}

	/**
	 * Connect to WebSocket server
	 */
	connect() {
		if (this.isConnecting || (this.ws && this.ws.readyState === WebSocket.OPEN)) {
			return
		}

		this.isConnecting = true
		this.isManualClose = false

		try {
			this.ws = new WebSocket(this.url)

			this.ws.onopen = (event) => {
				console.log('WebSocket connected', event)
				this.isConnecting = false
				this.reconnectAttempts = 0

				// Start heartbeat
				this.startHeartbeat()

				// Call onOpen handler
				if (this.onOpen) {
					this.onOpen(event)
				}
			}

			this.ws.onmessage = (event) => {
				try {
					const message = JSON.parse(event.data)
					console.log('WebSocket message received', message)

					// Handle different message types
					this.handleMessage(message)

					// Call onMessage handler
					if (this.onMessage) {
						this.onMessage(message, event)
					}
				} catch (error) {
					console.error('Failed to parse WebSocket message', error)
				}
			}

			this.ws.onerror = (error) => {
				console.error('WebSocket error', error)
				this.isConnecting = false

				// Stop heartbeat
				this.stopHeartbeat()

				// Call onError handler
				if (this.onError) {
					this.onError(error)
				}
			}

			this.ws.onclose = (event) => {
				console.log('WebSocket closed', event)
				this.isConnecting = false

				// Stop heartbeat
				this.stopHeartbeat()

				// Call onClose handler
				if (this.onClose) {
					this.onClose(event)
				}

				// Attempt to reconnect if not manually closed
				if (!this.isManualClose && this.reconnectAttempts < this.maxReconnectAttempts) {
					this.reconnect()
				}
			}
		} catch (error) {
			console.error('Failed to create WebSocket connection', error)
			this.isConnecting = false

			// Call onError handler
			if (this.onError) {
				this.onError(error)
			}
		}
	}

	/**
	 * Reconnect to WebSocket server
	 */
	reconnect() {
		if (this.isConnecting || this.reconnectAttempts >= this.maxReconnectAttempts) {
			console.log('Max reconnection attempts reached')
			return
		}

		this.reconnectAttempts++
		const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1)

		console.log(`Reconnecting attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts} in ${delay}ms`)

		setTimeout(() => {
			this.connect()
		}, delay)
	}

	/**
	 * Disconnect from WebSocket server
	 */
	disconnect() {
		this.isManualClose = true
		this.stopHeartbeat()

		if (this.ws) {
			this.ws.close()
			this.ws = null
		}
	}

	/**
	 * Send message to server
	 */
	send(type, data = {}) {
		if (this.ws && this.ws.readyState === WebSocket.OPEN) {
			const message = JSON.stringify({
				type,
				data,
			})
			this.ws.send(message)
			console.log('WebSocket message sent', { type, data })
		} else {
			console.warn('WebSocket is not connected')
		}
	}

	/**
	 * Handle incoming messages
	 */
	handleMessage(message) {
		const { type, data } = message

		// Get handler for this message type
		const handler = this.messageHandlers.get(type)
		if (handler) {
			handler(data)
		}
	}

	/**
	 * Register message handler
	 */
	on(messageType, handler) {
		this.messageHandlers.set(messageType, handler)
	}

	/**
	 * Unregister message handler
	 */
	off(messageType) {
		this.messageHandlers.delete(messageType)
	}

	/**
	 * Start heartbeat
	 */
	startHeartbeat() {
		this.stopHeartbeat()
		this.heartbeatTimer = setInterval(() => {
			this.send('heartbeat', { timestamp: Date.now() })
		}, this.heartbeatInterval)
	}

	/**
	 * Stop heartbeat
	 */
	stopHeartbeat() {
		if (this.heartbeatTimer) {
			clearInterval(this.heartbeatTimer)
			this.heartbeatTimer = null
		}
	}

	/**
	 * Get connection state
	 */
	get readyState() {
		if (!this.ws) return WebSocket.CLOSED
		return this.ws.readyState
	}

	/**
	 * Check if connected
	 */
	get isConnected() {
		return this.ws && this.ws.readyState === WebSocket.OPEN
	}
}

/**
 * Create WebSocket connection
 */
export function createWebSocket(userId, token, options = {}) {
	const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'

	// 优先使用配置的 WS_URL，否则使用当前域名
	const host = options.wsUrl || window.location.host
	const url = `${protocol}//${host}/ws?user_id=${userId}&token=${token}`

	return new WebSocketClient(url, options)
}

/**
 * WebSocket singleton instance with connection management
 */
let wsClient = null
let currentUserId = null
let currentToken = null
let pendingConnection = null

export function getWebSocket(userId, token, options = {}) {
	// Check if we have a connection for the same user
	if (wsClient) {
		// If connection exists and user/token matches
		if (currentUserId === userId && currentToken === token) {
			const state = wsClient.getConnectionState()

			// If already connected or connecting, return existing instance
			if (state === 'OPEN' || state === 'CONNECTING') {
				console.log('WebSocket: Reusing existing connection', {
					state,
					userId: currentUserId,
				})
				return wsClient
			}

			// If connection is closed or closing, clean up
			if (state === 'CLOSED' || state === 'CLOSING') {
				console.log('WebSocket: Cleaning up closed connection')
				if (wsClient.ws) {
					wsClient.ws.onopen = null
					wsClient.ws.onmessage = null
					wsClient.ws.onerror = null
					wsClient.ws.onclose = null
					wsClient.ws = null
				}
			}
		}

		// Different user/token, disconnect old connection
		if (currentUserId !== userId || currentToken !== token) {
			console.log('WebSocket: User changed, disconnecting old connection')
			if (wsClient) {
				wsClient.disconnect()
			}
			wsClient = null
		}
	}

	// Check if there's a pending connection
	if (pendingConnection) {
		return new Promise((resolve) => {
			const checkPending = setInterval(() => {
				if (!pendingConnection) {
					clearInterval(checkPending)
					console.log('WebSocket: Pending connection completed')
					resolve(getWebSocket(userId, token, options))
				}
			}, 100)
		})
	}

	// Create new connection
	console.log('WebSocket: Creating new connection', {
		userId,
		token: token ? '***' : null,
	})
	currentUserId = userId
	currentToken = token

	pendingConnection = true
	wsClient = createWebSocket(userId, token, options)

	// Mark connection as no longer pending when connected or failed
	const originalOnOpen = wsClient.onOpen
	const originalOnError = wsClient.onError
	const originalOnClose = wsClient.onClose

	wsClient.onOpen = (event) => {
		pendingConnection = null
		console.log('WebSocket: Connection established', { userId })
		if (originalOnOpen) originalOnOpen(event)
	}

	wsClient.onError = (error) => {
		pendingConnection = null
		console.error('WebSocket: Connection error', { userId, error })
		if (originalOnError) originalOnError(error)
	}

	wsClient.onClose = (event) => {
		pendingConnection = null
		console.log('WebSocket: Connection closed', {
			userId,
			code: event.code,
			reason: event.reason,
		})
		if (originalOnClose) originalOnClose(event)
	}

	return wsClient
}

export function closeWebSocket() {
	if (wsClient) {
		console.log('WebSocket: Closing connection manually')
		wsClient.disconnect()

		// Clean up event handlers
		if (wsClient.ws) {
			wsClient.ws.onopen = null
			wsClient.ws.onmessage = null
			wsClient.ws.onerror = null
			wsClient.ws.onclose = null
			wsClient.ws = null
		}

		wsClient = null
		currentUserId = null
		currentToken = null
		pendingConnection = null
	}
}

export default WebSocketClient
