'use strict';
/**
 * Decentralized sync for the desktop shell — the Node twin of Android's SyncCoordinator.java. Every player
 * can host the embedded broker (broker.js); the group elects a MASTER and fails over automatically. No
 * dedicated machine, no single point of failure.
 *
 * Election (deterministic by deviceId, peer-to-peer over the LAN): the master is the LOWEST-id device that
 * is alive. Walk the roster lowest-first; the first lower-id peer whose broker answers on the sync port is
 * the master (this device follows); if none answers, THIS device is the lowest alive → it hosts the broker.
 * Re-evaluated every EVAL_MS, so a dead master is detected and a new one elected (failover). The renderer
 * (web player) asks masterUrl() and (re)connects there.
 */
const net = require('net');
const { createBroker } = require('./broker');

const SYNC_PORT = 4800;
const EVAL_MS = 5000;
const PROBE_TIMEOUT_MS = 700;

function createSyncCoordinator() {
	const broker = createBroker({ port: SYNC_PORT });
	let running = false;
	let timer = null;
	let group = '';
	let selfId = '';
	let roster = []; // sorted [{deviceId, ip}]
	let masterUrl = null;

	function reachable(ip) {
		return new Promise((resolve) => {
			const s = net.connect({ host: ip, port: SYNC_PORT });
			let done = false;
			const fin = (v) => { if (done) return; done = true; try { s.destroy(); } catch (e) { /* */ } resolve(v); };
			s.setTimeout(PROBE_TIMEOUT_MS);
			s.on('connect', () => fin(true));
			s.on('timeout', () => fin(false));
			s.on('error', () => fin(false));
		});
	}

	async function elect() {
		for (const m of roster) {
			if (m.deviceId === selfId) {
				if (!broker.isRunning()) { broker.start(); console.log('[sync] elected MASTER (self=' + selfId + ')'); }
				setMaster('ws://127.0.0.1:' + SYNC_PORT);
				return;
			}
			if (m.ip && (await reachable(m.ip))) {
				if (broker.isRunning()) { broker.stop(); console.log('[sync] demoted, following ' + m.deviceId + ' @ ' + m.ip); }
				setMaster('ws://' + m.ip + ':' + SYNC_PORT);
				return;
			}
		}
		setMaster(null);
	}

	function setMaster(url) {
		if (url !== masterUrl) { masterUrl = url; console.log('[sync] master url = ' + url); }
	}

	function loop() {
		elect().catch((e) => console.error('[sync] elect', e.message)).finally(() => {
			if (running) timer = setTimeout(loop, EVAL_MS);
		});
	}

	return {
		configure(groupName, deviceId, rosterArr) {
			group = groupName || '';
			selfId = deviceId || '';
			const list = Array.isArray(rosterArr) ? rosterArr.filter((m) => m && m.deviceId).map((m) => ({ deviceId: String(m.deviceId), ip: String(m.ip || '') })) : [];
			if (!list.some((m) => m.deviceId === selfId) && selfId) list.push({ deviceId: selfId, ip: '127.0.0.1' });
			list.sort((a, b) => (a.deviceId < b.deviceId ? -1 : a.deviceId > b.deviceId ? 1 : 0));
			roster = list;
			console.log('[sync] configure group=' + group + ' self=' + selfId + ' members=' + list.length);
			if (!group) { this.stop(); return; }
			if (!running) { running = true; loop(); } else { elect().catch(() => {}); }
		},
		masterUrl() { return masterUrl; },
		stop() {
			running = false;
			if (timer) { clearTimeout(timer); timer = null; }
			broker.stop();
			masterUrl = null;
		},
	};
}

module.exports = { createSyncCoordinator };
