import WebSocket from 'ws'

const API_URL = process.env.API_URL || 'http://localhost:3456/api/v1'

export interface WsMessage {
	event?: string
	action?: string
	success?: boolean
	error?: string
	data?: unknown
}

/**
 * Returns the WebSocket URL derived from the API base URL.
 */
export function getWsUrl(): string {
	return API_URL.replace(/\/+$/, '').replace(/^http/, 'ws') + '/ws'
}

/**
 * Opens a raw WebSocket connection to the API.
 */
export function openWs(): Promise<WebSocket> {
	return new Promise((resolve, reject) => {
		const ws = new WebSocket(getWsUrl())
		ws.on('open', () => resolve(ws))
		ws.on('error', reject)
	})
}

/**
 * Waits for the next message on a WebSocket connection.
 */
export function waitForMessage(ws: WebSocket, timeout = 5000): Promise<WsMessage> {
	return new Promise((resolve, reject) => {
		const timer = setTimeout(() => reject(new Error('WebSocket message timeout')), timeout)
		ws.once('message', (data) => {
			clearTimeout(timer)
			resolve(JSON.parse(data.toString()))
		})
	})
}

/**
 * Sends a JSON message on the WebSocket.
 */
export function sendMessage(ws: WebSocket, msg: object): void {
	ws.send(JSON.stringify(msg))
}

/**
 * Authenticates a WebSocket connection and returns the auth.success message.
 */
export async function authenticateWs(ws: WebSocket, token: string): Promise<WsMessage> {
	sendMessage(ws, {action: 'auth', token})
	const msg = await waitForMessage(ws)
	if (msg.action !== 'auth.success') {
		throw new Error(`Expected auth.success, got: ${JSON.stringify(msg)}`)
	}
	return msg
}

/**
 * Subscribes to an event on an authenticated WebSocket connection.
 */
export function subscribeWs(ws: WebSocket, event: string): void {
	sendMessage(ws, {action: 'subscribe', event})
}

/**
 * Collects all messages received within a time window.
 */
export function collectMessages(ws: WebSocket, duration: number): Promise<WsMessage[]> {
	return new Promise((resolve) => {
		const messages: WsMessage[] = []
		const handler = (data: WebSocket.Data) => {
			messages.push(JSON.parse(data.toString()))
		}
		ws.on('message', handler)
		setTimeout(() => {
			ws.off('message', handler)
			resolve(messages)
		}, duration)
	})
}

/**
 * Closes a WebSocket connection safely.
 */
export function closeWs(ws: WebSocket): void {
	if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
		ws.close()
	}
}
