import { parseWsEnvelope } from './liveSocketProtocol.js'

/** 本地 fs-live-ws 默认地址（/live 未返回 wsUrl 时的兜底） */
const FALLBACK_WS_PATH = '/ws/app/webSocket'

/** 直播间 WebSocket（fs-live-ws：JWT 鉴权 + 节点发现） */
export class LiveSocketManager {
	constructor(hooks = {}) {
		this.hooks = hooks
		this.socket = null
		this.pendingSocketTask = null
		this.isSocketOpen = false
		this.isConnecting = false
		this.isManualClose = false
		this.reconnectCount = 0
		this.maxReconnectAttempts = 5
		this.reconnectTimer = null
		this.connectTimeoutTimer = null
		this.networkRetryTimer = null
		this.heartBeatTimer = null
		this.pingTimeoutTimer = null
		this.heartBeatRetryCount = 0
		this.maxHeartBeatRetries = 3
		this.heartBeatInterval = 15000
		this.adaptiveHeartBeatInterval = 15000
		this.pingTimeout = 25000
		this.connectionStartTime = 0
		this.connectionLatency = 0
		this.messageCount = 0
		this.errorCount = 0
		this.lastHeartBeatTime = 0
		this.resolvedWsBase = hooks.wsBaseUrl || ''
		this._suppressReconnect = false
		// fs-live-ws：7115=Tomcat HTTP，7116=Netty WebSocket
		this.fallbackWsHost = hooks.fallbackWsHost || 'ws://127.0.0.1:7116'
	}

	isAvailable() {
		if (!this.socket || !this.isSocketOpen) {
			return false
		}
		// 微信小程序 SocketTask 的 readyState 可能不存在或与浏览器不一致，以 onOpen 标记为准
		const state = this.socket.readyState
		if (state === undefined || state === null) {
			return true
		}
		return state === 1 || state === 'OPEN'
	}

	getStats() {
		const now = Date.now()
		const uptime = this.connectionStartTime > 0 ? now - this.connectionStartTime : 0
		return {
			connectionLatency: this.connectionLatency,
			uptime,
			messageCount: this.messageCount,
			errorCount: this.errorCount,
			errorRate: this.messageCount > 0 ? (this.errorCount / this.messageCount * 100).toFixed(2) : 0,
			messagesPerSecond: uptime > 0 ? (this.messageCount / (uptime / 1000)).toFixed(2) : 0,
			isConnected: this.isAvailable(),
			reconnectCount: this.reconnectCount
		}
	}

	resetState() {
		this._forceCloseAll(true)
		this.isManualClose = true
		this.reconnectCount = 0
		this.heartBeatRetryCount = 0
		this.lastHeartBeatTime = 0
		this.adaptiveHeartBeatInterval = this.heartBeatInterval
		this.connectionStartTime = 0
		this.connectionLatency = 0
		this.messageCount = 0
		this.errorCount = 0
		this.resolvedWsBase = this.hooks.wsBaseUrl || ''
		this.cleanupTimers()
	}

	destroy() {
		this.close(true)
		this.cleanupTimers()
		if (this.networkRetryTimer) {
			clearTimeout(this.networkRetryTimer)
			this.networkRetryTimer = null
		}
	}

	connect() {
		if (this.isConnecting || this.pendingSocketTask) {
			return
		}
		if (!this._networkAvailable()) {
			if (!this.networkRetryTimer) {
				this.networkRetryTimer = setTimeout(() => {
					this.networkRetryTimer = null
					this.connect()
				}, 3000)
			}
			return
		}
		if (this.socket && this.isAvailable()) {
			this.reconnectCount = 0
			return
		}
		if (this.socket && (this.socket.readyState === 1 || this.socket.readyState === 'OPEN')) {
			this.isSocketOpen = true
			this.isConnecting = false
			this.reconnectCount = 0
			return
		}
		if (this.socket && (this.socket.readyState === 0 || this.socket.readyState === 1)) {
			this.close(false)
			setTimeout(() => this._createConnection(), 100)
			return
		}
		this._createConnection()
	}

	triggerReconnect() {
		this._handleReconnect()
	}

	close(isManual = true) {
		if (!this.socket && !this.pendingSocketTask && !this.isSocketOpen && !this.isConnecting) return
		this.isManualClose = isManual
		this._forceCloseAll(false)
	}

	/** 关闭本地 SocketTask，并尝试关闭微信全局 WS（避免 CONNECTING 残留触发 clickCheckTask） */
	_forceCloseAll(useUniCloseSocket = true) {
		this.cleanupTimers()
		this._clearConnectTimeout()
		const tasks = []
		if (this.socket) tasks.push(this.socket)
		if (this.pendingSocketTask && this.pendingSocketTask !== this.socket) {
			tasks.push(this.pendingSocketTask)
		}
		this.socket = null
		this.pendingSocketTask = null
		this.isSocketOpen = false
		this.isConnecting = false
		tasks.forEach((task) => {
			try {
				if (task && typeof task.close === 'function') {
					task.close({ code: 1000, reason: 'close' })
				}
			} catch (err) {
				console.warn('[LiveSocket] close task failed', err)
			}
		})
		if (useUniCloseSocket) {
			try {
				uni.closeSocket({})
			} catch (err) {
				// 无活跃连接时会失败，可忽略
			}
		}
	}

	/** uni.connectSocket 必须带 success/fail/complete 之一，否则可能返回 Promise 而非 SocketTask */
	_createSocketTask(url) {
		return new Promise((resolve, reject) => {
			let settled = false
			const finish = (task, err) => {
				if (settled) return
				settled = true
				if (err) reject(err)
				else resolve(task)
			}
			const options = {
				url,
				success: () => {},
				fail: (err) => finish(null, err || new Error('connectSocket fail')),
				complete: () => {}
			}
			let result
			try {
				result = uni.connectSocket(options)
			} catch (err) {
				finish(null, err)
				return
			}
			if (result && typeof result.then === 'function') {
				result.then((task) => {
					if (task && typeof task.onOpen === 'function') {
						finish(task)
					} else {
						finish(null, new Error('connectSocket Promise 未解析为 SocketTask'))
					}
				}).catch((err) => finish(null, err))
				return
			}
			if (result && typeof result.onOpen === 'function') {
				finish(result)
				return
			}
			finish(null, new Error('connectSocket 未返回 SocketTask'))
		})
	}

	_bindSocketTask(socketTask) {
		this.pendingSocketTask = socketTask
		this._startConnectTimeout(socketTask)

		socketTask.onOpen(() => {
			if (this.pendingSocketTask !== socketTask && this.socket !== socketTask) return
			this._clearConnectTimeout()
			this.pendingSocketTask = null
			this.socket = socketTask
			this.isConnecting = false
			this.isSocketOpen = true
			if (this.connectionStartTime > 0) {
				this.connectionLatency = Date.now() - this.connectionStartTime
			}
			this.reconnectCount = 0
			this._resetReconnectState()
			this.heartBeatRetryCount = 0
			console.log('[LiveSocket] onOpen', { latency: this.connectionLatency })
			this.hooks.onOpen?.()
			this._startHeartBeat()
		})

		socketTask.onMessage((res) => {
			this.messageCount++
			try {
				const parsed = parseWsEnvelope(res.data)
				if (parsed?.socketMessage?.cmd === 'heartbeat') {
					if (this.pingTimeoutTimer) {
						clearTimeout(this.pingTimeoutTimer)
						this.pingTimeoutTimer = null
					}
					this.heartBeatRetryCount = 0
					this._adjustHeartBeatInterval(true)
					return
				}
				this.hooks.onMessage?.(res)
			} catch (err) {
				console.error('消息解析异常:', err)
				this.errorCount++
			}
		})

		socketTask.onError((err) => {
			console.error('[LiveSocket] onError', err)
			this.errorCount++
			this.isSocketOpen = false
			this.isConnecting = false
			this._clearConnectTimeout()
			if (this.pendingSocketTask === socketTask) {
				this.pendingSocketTask = null
			}
			if (this.socket === socketTask) {
				this.socket = null
			}
			this._stopHeartBeat()
			this._handleConnectionError('连接错误', err)
		})

		socketTask.onClose((res) => {
			console.log('[LiveSocket] onClose', res?.code, res?.reason)
			this.isSocketOpen = false
			this.isConnecting = false
			this._clearConnectTimeout()
			if (this.pendingSocketTask === socketTask) {
				this.pendingSocketTask = null
			}
			if (this.socket === socketTask) {
				this.socket = null
			}
			this._stopHeartBeat()
			this.hooks.onClose?.(res)
			if (!this.isManualClose && !this._suppressReconnect && res.code !== 1000) {
				this._handleReconnect()
			}
		})
	}

	_startConnectTimeout(socketTask) {
		this._clearConnectTimeout()
		this.connectTimeoutTimer = setTimeout(() => {
			this.connectTimeoutTimer = null
			if (!this.isSocketOpen && (this.pendingSocketTask === socketTask || this.socket === socketTask)) {
				console.warn('[LiveSocket] connect timeout, force close')
				this.close(false)
				this.isManualClose = false
				this._handleReconnect()
			}
		}, 15000)
	}

	_clearConnectTimeout() {
		if (this.connectTimeoutTimer) {
			clearTimeout(this.connectTimeoutTimer)
			this.connectTimeoutTimer = null
		}
	}

	send(data) {
		return new Promise((resolve, reject) => {
			if (!this.isAvailable()) {
				reject(new Error('socket not ready'))
				return
			}
			const payload = typeof data === 'string' ? data : JSON.stringify(data)
			try {
				this.socket.send({
					data: payload,
					success: resolve,
					fail: reject
				})
			} catch (err) {
				reject(err)
			}
		})
	}

	adjustHeartBeatForNetworkType(networkType) {
		let newInterval = this.heartBeatInterval
		switch (networkType) {
			case '2g': newInterval = 30000; break
			case '3g': newInterval = 20000; break
			case '4g':
			case '5g': newInterval = 15000; break
			case 'wifi': newInterval = 10000; break
			default: newInterval = 15000
		}
		if (newInterval !== this.adaptiveHeartBeatInterval) {
			this.adaptiveHeartBeatInterval = newInterval
			if (this.heartBeatTimer && this.isAvailable()) {
				this._startHeartBeat()
			}
		}
	}

	async _resolveWsBaseUrl(liveId) {
		if (typeof this.hooks.getWsBaseUrl === 'function') {
			const dynamic = this.hooks.getWsBaseUrl()
			if (dynamic) {
				return String(dynamic).split('?')[0]
			}
		}
		if (this.hooks.wsBaseUrl) {
			return String(this.hooks.wsBaseUrl).split('?')[0]
		}
		return `${this.fallbackWsHost}${FALLBACK_WS_PATH}`
	}

	_buildConnectUrl(baseUrl, config) {
		const {
			liveId,
			userId,
			userType = 0,
			qrFrom = '',
			urlOption = {},
			address = ''
		} = config
		const token = this.hooks.getToken?.() || uni.getStorageSync('AppToken') || ''
		const params = [
			`liveId=${encodeURIComponent(liveId)}`,
			`userId=${encodeURIComponent(userId)}`,
			`userType=${encodeURIComponent(userType)}`,
			`APPToken=${encodeURIComponent(token)}`
		]
		if (address) params.push(`location=${encodeURIComponent(address)}`)
		let wsUrl = `${baseUrl}?${params.join('&')}`
		if (qrFrom) wsUrl += qrFrom
		if (urlOption.qwUserId) wsUrl += `&qwUserId=${urlOption.qwUserId}`
		if (urlOption.qwExternalId) wsUrl += `&externalContactId=${urlOption.qwExternalId}`
		return wsUrl
	}

	async _createConnection() {
		const config = typeof this.hooks.getConnectConfig === 'function' ? this.hooks.getConnectConfig() : {}
		const { liveId, userId } = config
		if (!liveId || !userId) {
			console.error('WebSocket 缺少 liveId 或 userId')
			return
		}
		const token = this.hooks.getToken?.() || uni.getStorageSync('AppToken') || ''
		if (!token) {
			console.error('WebSocket 缺少 APPToken')
			return
		}

		this.connectionStartTime = Date.now()
		this._resetReconnectState()
		this._suppressReconnect = true
		this._forceCloseAll(true)
		this._suppressReconnect = false
		this.isConnecting = true
		this.isSocketOpen = false

		let wsUrl
		try {
			const baseUrl = await this._resolveWsBaseUrl(liveId)
			wsUrl = this._buildConnectUrl(baseUrl, config)
			console.log('[LiveSocket] connect', wsUrl.replace(/APPToken=[^&]+/, 'APPToken=***'))
			if (wsUrl.startsWith('ws://')) {
				console.warn('[LiveSocket] 使用 ws:// 地址，微信开发者工具需勾选「不校验合法域名」')
			}
		} catch (err) {
			console.error('构建 WebSocket 地址失败', err)
			this.isConnecting = false
			this._handleConnectionError('地址解析失败', err)
			return
		}

		try {
			const socketTask = await this._createSocketTask(wsUrl)
			this._bindSocketTask(socketTask)
		} catch (e) {
			console.error('[LiveSocket] 创建连接失败:', e)
			this.isConnecting = false
			this.pendingSocketTask = null
			this._handleConnectionError('连接请求失败', e)
		}
	}

	_startHeartBeat() {
		this._stopHeartBeat()
		this.heartBeatTimer = setInterval(() => this._sendHeartBeat(), this.adaptiveHeartBeatInterval)
	}

	_stopHeartBeat() {
		if (this.heartBeatTimer) {
			clearInterval(this.heartBeatTimer)
			this.heartBeatTimer = null
		}
		if (this.pingTimeoutTimer) {
			clearTimeout(this.pingTimeoutTimer)
			this.pingTimeoutTimer = null
		}
	}

	_sendHeartBeat() {
		if (!this.isAvailable() || !this._networkAvailable()) return
		this.lastHeartBeatTime = Date.now()
		const config = this.hooks.getConnectConfig?.() || {}
		const watchDuration = this.hooks.getWatchDuration?.() || 0
		const networkType = this.hooks.getNetworkType?.() || 'unknown'
		const heartBeatMsg = JSON.stringify({
			cmd: 'heartbeat',
			msg: 'ping',
			userId: config.userId || '',
			liveId: config.liveId || '',
			timestamp: this.lastHeartBeatTime,
			networkType,
			data: String(watchDuration)
		})
		const onFail = () => {
			this.heartBeatRetryCount++
			this._adjustHeartBeatInterval(false)
			const retryDelay = this._getRetryDelay(networkType)
			if (this.heartBeatRetryCount < this.maxHeartBeatRetries) {
				setTimeout(() => this._sendHeartBeat(), retryDelay)
			} else {
				this.heartBeatRetryCount = 0
				this._handleReconnect()
			}
		}
		try {
			this.socket.send({
				data: heartBeatMsg,
				success: () => {
					this.heartBeatRetryCount = 0
					this._adjustHeartBeatInterval(true)
					this._startPingTimeout()
				},
				fail: onFail
			})
		} catch (err) {
			onFail()
		}
	}

	_getRetryDelay(networkType) {
		const baseDelay = 2000
		const retryMultiplier = Math.pow(1.5, this.heartBeatRetryCount)
		let networkMultiplier = 1.5
		if (networkType === '2g') networkMultiplier = 3
		else if (networkType === '3g') networkMultiplier = 2
		else if (networkType === 'wifi') networkMultiplier = 0.8
		return Math.min(baseDelay * retryMultiplier * networkMultiplier, 10000)
	}

	_adjustHeartBeatInterval(isSuccess) {
		if (isSuccess) {
			this.adaptiveHeartBeatInterval = Math.min(this.adaptiveHeartBeatInterval * 1.1, 30000)
		} else {
			this.adaptiveHeartBeatInterval = Math.max(this.adaptiveHeartBeatInterval * 0.9, 10000)
		}
	}

	_startPingTimeout() {
		if (this.pingTimeoutTimer) {
			clearTimeout(this.pingTimeoutTimer)
			this.pingTimeoutTimer = null
		}
		this.pingTimeoutTimer = setTimeout(() => {
			this.pingTimeoutTimer = null
			this.heartBeatRetryCount++
			if (this.heartBeatRetryCount < this.maxHeartBeatRetries) {
				setTimeout(() => this._sendHeartBeat(), 1000)
			} else {
				this.heartBeatRetryCount = 0
				this._handleReconnect()
			}
		}, this.pingTimeout)
	}

	_handleReconnect() {
		if (this.isManualClose || this.reconnectTimer) return
		this._stopHeartBeat()
		this.resolvedWsBase = ''
		if (!this._networkAvailable()) {
			this.reconnectTimer = setTimeout(() => {
				this.reconnectTimer = null
				this._handleReconnect()
			}, 5000)
			return
		}
		if (this.reconnectCount < this.maxReconnectAttempts) {
			this.reconnectCount++
			const baseWait = this.reconnectCount <= 1 ? 500 : 10000
			const totalDelay = Math.min(1000 * Math.pow(2, this.reconnectCount - 1) + baseWait + Math.random() * 1000, 30000)
			this.reconnectTimer = setTimeout(() => {
				this.reconnectTimer = null
				if (this._networkAvailable() && !this.isManualClose && !this.isAvailable()) {
					this.connect()
				} else if (this.isAvailable()) {
					this.reconnectCount = 0
				} else {
					this._handleReconnect()
				}
			}, totalDelay)
		} else {
			uni.showToast({ title: '网络连接异常，请检查网络后重新进入', icon: 'none', duration: 3000 })
		}
	}

	_handleConnectionError(errorType, error) {
		console.error(`WebSocket ${errorType}:`, error)
		if (errorType === '连接请求失败' && !this._networkAvailable()) return
		this._handleReconnect()
	}

	_resetReconnectState() {
		if (this.reconnectTimer) {
			clearTimeout(this.reconnectTimer)
			this.reconnectTimer = null
		}
		this.heartBeatRetryCount = 0
		this.adaptiveHeartBeatInterval = this.heartBeatInterval
	}

	cleanupTimers() {
		this._stopHeartBeat()
		this._resetReconnectState()
	}

	_networkAvailable() {
		return this.hooks.getNetworkAvailable?.() !== false
	}
}

export function createLiveSocket(hooks) {
	return new LiveSocketManager(hooks)
}
