// form/nc/nc-admin/nc-nats/nc-nats.js
// NATS Testing, Statistics, and Controlling Admin Page

const nats = nc.nats
let gridSubscription = null
let statsUpdateInterval = null

onMounted(async () => {
	connectToNats()
	updateStatLabels()
	watch(
		() => [rec.stat_sent_count, rec.stat_received_count, rec.stat_message_rate, rec.stat_sent_bytes, rec.stat_received_bytes, rec.test_progress, rec.test_total_rows],
		updateStatLabels
	)
})

onUnmounted(() => {
	if (statsUpdateInterval) {
		clearInterval(statsUpdateInterval)
	}
	if (gridSubscription) {
		try {
			gridSubscription.unsubscribe()
		} catch (err) {
			console.warn('NATS: error unsubscribing:', err)
		}
	}
})

function updateStatLabels() {
	rec.nats_stat_sent = `${rec.stat_sent_count} ${hdr.nats.sent_count}`
	rec.nats_stat_received = `${rec.stat_received_count} ${hdr.nats.received_count}`
	rec.nats_stat_rate = `${rec.stat_message_rate} ${hdr.nats.per_second}`
	rec.nats_stat_sent_bytes = formatBytes(rec.stat_sent_bytes)
	rec.nats_stat_received_bytes = formatBytes(rec.stat_received_bytes)
	rec.nats_progress_label = `${rec.test_progress} / ${rec.test_total_rows}`
}

async function connectToNats() {
	const natsConnection = await nats.connectNats()
	if (natsConnection) {
		rec.nats_connected = true
		rec.nats_connection_info = natsConnection.getServer()
		subscribeToNatsBucket(natsConnection)
	} else {
		rec.nats_connected = false
		rec.nats_connection_info = 'Connection failed'
	}
}

function subscribeToNatsBucket(natsConnection) {
	if (!natsConnection) {
		console.warn('NATS: cannot subscribe, no connection')
		return
	}
	gridSubscription = nats.subscribeToGrid(natsConnection, rec.test_bucket, handleNatsMessage)
	console.log(`NATS: subscribed to bucket '${rec.test_bucket}'`)
}

function handleNatsMessage(gridData) {
	if (!gridData || !gridData.data) {
		return
	}
	const newData = gridData.data
	const existingData = state.grid.test_message?.data || []
	rec.stat_received_count += newData.length
	rec.stat_received_bytes = calculateBytes(newData)
	const combinedData = [...existingData, ...newData]
	nc.grid.dataChanged(state.grid.test_message, combinedData)
	const changedRowArray = detectChangedRows(existingData, newData)
	if (changedRowArray.length > 0) {
		rec.grid_changed_row = changedRowArray.join(',')
		highlightChangedRows(changedRowArray)
	}
	rec.grid_row_count = combinedData.length
	rec.test_progress = combinedData.length
	rec.stat_message_rate = calculateMessageRate()
	console.log(`NATS: received ${newData.length} rows from message ${gridData.message_id}, total ${combinedData.length}`)
}

function detectChangedRows(currentData, newData) {
	const changed = []
	const currentIdx = {}
	for (const row of currentData) {
		if (row.record_id) {
			currentIdx[row.record_id] = row
		}
	}
	for (const row of newData) {
		if (row.record_id && currentIdx[row.record_id]) {
			if (!rowsEqual(currentIdx[row.record_id], row)) {
				row.__changed = true
				changed.push(row.idx)
			}
		}
	}
	return changed
}

function rowsEqual(row1, row2) {
	const keys = ['resource_id', 'product_id', 'work_phase_state', 'amount']
	for (const key of keys) {
		if (row1[key] !== row2[key]) {
			return false
		}
	}
	return true
}

function calculateBytes(data) {
	let total = 0
	for (const row of data) {
		total += JSON.stringify(row).length
	}
	return total
}

function formatBytes(byteCount) {
	if (byteCount == null) {
		return '0 B'
	}
	const units = ['B', 'KB', 'MB', 'GB']
	let size = byteCount
	let unitIdx = 0
	while (size >= 1024 && unitIdx < units.length - 1) {
		size /= 1024
		unitIdx++
	}
	return `${size.toFixed(1)} ${units[unitIdx]}`
}

function getRowCountForTest(testType, customRowCount) {
	const rowCountMap = {
		basic: 5000,
		multi: 75000,
		large: 300000,
		stress_small: 1000,
		stress_medium: 10000,
		stress_large: 100000
	}
	if (testType === 'custom') {
		return customRowCount || 5000
	}
	return rowCountMap[testType] || 5000
}

function calculateMessageRate() {
	const elapsed = (Date.now() - rec.stat_start_time) / 1000
	return elapsed > 0 ? Math.round(rec.stat_received_count / elapsed) : 0
}

async function startTest() {
	if (rec.test_running) {
		return
	}
	rec.test_running = true
	rec.stat_start_time = Date.now()
	rec.test_total_rows = getRowCountForTest(rec.test_type, rec.test_row_count)
	rec.test_progress = 0
	rec.stat_sent_count = 0
	rec.stat_received_count = 0
	rec.stat_sent_bytes = 0
	rec.stat_received_bytes = 0
	rec.stat_message_rate = 0
	rec.grid_changed_row = ''
	nc.grid.dataChanged(state.grid.test_message, [])
	nc.callServer(
		state,
		'nats',
		{
			action: 'send_test',
			rec: {
				test_type: rec.test_type,
				test_bucket: rec.test_bucket,
				row_count: rec.test_total_rows
			}
		},
		testResponse
	)
	startStatsUpdate()
}

function testResponse(response) {
	if (response.error) {
		message.setError(state, `NATS test error: ${response.error.description || response.error.message || 'Unknown error'}`)
		rec.test_running = false
		return
	}
	if (response.rec) {
		if (response.rec.stat_sent_count != null) {
			rec.stat_sent_count = response.rec.stat_sent_count
		}
		if (response.rec.test_complete) {
			rec.test_running = false
			rec.stat_end_time = Date.now()
			rec.stat_message_rate = calculateMessageRate()
			message.setInfo(state, `NATS test complete: sent ${rec.stat_sent_count} messages, received ${rec.stat_received_count} messages`)
		}
	}
}

function stopTest() {
	if (!rec.test_running) {
		return
	}
	rec.test_running = false
	rec.stat_end_time = Date.now()
	rec.stat_message_rate = calculateMessageRate()
	stopStatsUpdate()
	message.setInfo(state, `NATS test stopped: sent ${rec.stat_sent_count}, received ${rec.stat_received_count} messages`)
}

function clearGrid() {
	nc.grid.dataChanged(state.grid.test_message, [])
	rec.grid_row_count = 0
	rec.test_progress = 0
	rec.test_total_rows = 0
	rec.stat_sent_count = 0
	rec.stat_received_count = 0
	rec.stat_sent_bytes = 0
	rec.stat_received_bytes = 0
	rec.stat_message_rate = 0
	rec.grid_changed_row = ''
	message.setInfo(state, 'NATS grid cleared')
}

function startStatsUpdate() {
	stopStatsUpdate()
	statsUpdateInterval = setInterval(() => {
		if (rec.test_running) {
			rec.stat_message_rate = calculateMessageRate()
		}
	}, 1000)
}

function stopStatsUpdate() {
	if (statsUpdateInterval) {
		clearInterval(statsUpdateInterval)
		statsUpdateInterval = null
	}
}

function highlightChangedRows(rowIdxArray) {
	for (const idx of rowIdxArray) {
		const row = state.grid.test_message?.data?.[idx - 1]
		if (row) {
			row.__changed = true
		}
	}
	setTimeout(() => {
		for (const idx of rowIdxArray) {
			const row = state.grid.test_message?.data?.[idx - 1]
			if (row) {
				row.__changed = false
			}
		}
		nc.grid.refreshCells(state, 'test_message')
	}, 1000)
}

function gridRowClicked(area, rowIndex, event, selected) {
	console.log(`NATS: grid row clicked: area=${area}, rowIndex=${rowIndex}, selected=${selected}`)
}

function tabChanged(action) {
	console.log(`NATS: tab changed: ${action}`)
}
