'use strict';
// Home Assistant via MQTT — minimale MQTT 3.1.1-client (geen dependencies) + HA-discovery.
// Publiceert rails-vermogen, wissels, bezetmelders en loc-snelheden als HA-entiteiten
// en luistert op commando-topics zodat HA de baan kan besturen.
const net = require('net');

// ---- minimale MQTT-client (QoS 0) -------------------------------------
function mqttConnect(o) {
  const host = o.host, port = o.port || 1883;
  let sock = null, connected = false, buf = Buffer.alloc(0), ping = null;
  const onMessage = o.onMessage || (() => {});
  const onConnect = o.onConnect || (() => {});
  const log = o.log || (() => {});

  function encLen(n) { const out = []; do { let b = n % 128; n = Math.floor(n / 128); if (n > 0) b |= 0x80; out.push(b); } while (n > 0); return Buffer.from(out); }
  function str(s) { const b = Buffer.from(s, 'utf8'); return Buffer.concat([Buffer.from([(b.length >> 8) & 0xff, b.length & 0xff]), b]); }

  function send(type, flags, payload) {
    if (!sock) return;
    const header = Buffer.from([(type << 4) | flags]);
    try { sock.write(Buffer.concat([header, encLen(payload.length), payload])); } catch (_) {}
  }
  function publish(topic, message, retain) {
    send(3, retain ? 1 : 0, Buffer.concat([str(topic), Buffer.from(String(message), 'utf8')]));
  }
  function subscribe(filter) {
    const pid = Buffer.from([0x00, 0x01]);
    send(8, 2, Buffer.concat([pid, str(filter), Buffer.from([0x00])]));
  }

  function handlePublish(payload) {
    const tlen = (payload[0] << 8) | payload[1];
    const topic = payload.slice(2, 2 + tlen).toString('utf8');
    const msg = payload.slice(2 + tlen).toString('utf8');     // QoS 0 → geen packet-id
    onMessage(topic, msg);
  }
  function parse() {
    while (buf.length >= 2) {
      let mult = 1, len = 0, i = 1, b;
      do { if (i >= buf.length) return; b = buf[i++]; len += (b & 0x7f) * mult; mult *= 128; } while (b & 0x80);
      if (buf.length < i + len) return;
      const type = buf[0] >> 4, payload = buf.slice(i, i + len);
      buf = buf.slice(i + len);
      if (type === 2) { connected = true; onConnect(); }          // CONNACK
      else if (type === 3) handlePublish(payload);                 // PUBLISH
    }
  }

  function open() {
    sock = net.connect(port, host, () => {
      const flags = 0x02 | (o.username ? 0x80 : 0) | (o.password ? 0x40 : 0);   // clean session + auth
      let payload = str(o.clientId || 'modelrailpro');
      if (o.username) payload = Buffer.concat([payload, str(o.username)]);
      if (o.password) payload = Buffer.concat([payload, str(o.password)]);
      const vh = Buffer.concat([str('MQTT'), Buffer.from([0x04, flags, 0x00, 0x3c])]);   // protocol 4, keepalive 60s
      send(1, 0, Buffer.concat([vh, payload]));
      ping = setInterval(() => send(12, 0, Buffer.alloc(0)), 45000);
    });
    sock.on('data', (d) => { buf = Buffer.concat([buf, d]); parse(); });
    sock.on('error', (e) => { log('MQTT-fout: ' + (e.message || e)); });
    sock.on('close', () => { connected = false; clearInterval(ping); });
  }
  function close() { try { clearInterval(ping); sock && sock.destroy(); } catch (_) {} connected = false; }
  open();
  return { publish, subscribe, close, isConnected: () => connected };
}

// ---- Home Assistant-integratie ----------------------------------------
function createHomeAssistant(opts) {
  const log = opts.log || (() => {});
  const command = opts.command || (() => {});     // (kind, args) → server voert uit
  let cfg = {}, cli = null, lastState = {};
  const PFX = 'modelrailpro';
  const DPFX = 'homeassistant';

  function dev() { return { identifiers: ['modelrailpro'], name: 'ModelRailPro', manufacturer: 'ModelRailPro', model: 'Baanbesturing' }; }

  function discover(snap) {
    if (!cli) return;
    // rails-vermogen
    cli.publish(DPFX + '/switch/mrp/power/config', JSON.stringify({ name: 'Rails', uniq_id: 'mrp_power', stat_t: PFX + '/power/state', cmd_t: PFX + '/power/set', pl_on: 'on', pl_off: 'off', dev: dev() }), true);
    for (const t of (snap.turnoutsList || [])) {
      cli.publish(DPFX + '/switch/mrp/turnout_' + t + '/config', JSON.stringify({ name: 'Wissel ' + t, uniq_id: 'mrp_turnout_' + t, stat_t: PFX + '/turnout/' + t + '/state', cmd_t: PFX + '/turnout/' + t + '/set', pl_on: 'thrown', pl_off: 'closed', dev: dev() }), true);
    }
    for (const b of (snap.blocksList || [])) {
      cli.publish(DPFX + '/binary_sensor/mrp/block_' + b + '/config', JSON.stringify({ name: 'Blok ' + b, uniq_id: 'mrp_block_' + b, stat_t: PFX + '/block/' + b + '/state', pl_on: 'busy', pl_off: 'free', dev_cla: 'occupancy', dev: dev() }), true);
    }
    for (const a of (snap.locoAddrs || [])) {
      cli.publish(DPFX + '/number/mrp/loco_' + a + '/config', JSON.stringify({ name: 'Loc ' + a + ' snelheid', uniq_id: 'mrp_loco_' + a, stat_t: PFX + '/loco/' + a + '/state', cmd_t: PFX + '/loco/' + a + '/set', min: 0, max: 126, dev: dev() }), true);
    }
    cli.subscribe(PFX + '/+/set');
    cli.subscribe(PFX + '/+/+/set');
    log('🏠 Home Assistant: entiteiten gepubliceerd');
  }

  function pub(key, topic, val) { if (cli && lastState[key] !== val) { lastState[key] = val; cli.publish(topic, val, true); } }

  function publishState(snap) {
    if (!cli || !cli.isConnected()) return;
    pub('power', PFX + '/power/state', snap.power ? 'on' : 'off');
    for (const t of Object.keys(snap.turnouts || {})) pub('t' + t, PFX + '/turnout/' + t + '/state', snap.turnouts[t] ? 'thrown' : 'closed');
    for (const b of Object.keys(snap.sensors || {})) pub('b' + b, PFX + '/block/' + b + '/state', snap.sensors[b] ? 'busy' : 'free');
    for (const a of Object.keys(snap.locos || {})) pub('l' + a, PFX + '/loco/' + a + '/state', String((snap.locos[a] || {}).speed || 0));
  }

  function onMsg(topic, msg) {
    const p = topic.split('/');     // modelrailpro/<kind>/<id?>/set
    if (p[0] !== PFX || p[p.length - 1] !== 'set') return;
    if (p[1] === 'power') command('power', { on: /on|1|true/i.test(msg) });
    else if (p[1] === 'turnout') command('turnout', { id: p[2], thrown: /thrown|1|true/i.test(msg) });
    else if (p[1] === 'loco') command('throttle', { addr: parseInt(p[2], 10), speed: Math.max(0, Math.min(126, parseInt(msg, 10) || 0)) });
  }

  function start(snap) {
    stop();
    if (!cfg.host) return;
    cli = mqttConnect({ host: cfg.host, port: cfg.port, username: cfg.username, password: cfg.password, clientId: 'modelrailpro', log,
      onConnect: () => { discover(snap); publishState(snap); }, onMessage: onMsg });
    log('🏠 Home Assistant: verbinden met MQTT ' + cfg.host + ':' + (cfg.port || 1883) + '…');
  }
  function stop() { if (cli) { try { cli.close(); } catch (_) {} cli = null; lastState = {}; } }
  function configure(c, snap) { const was = cfg; cfg = c || {}; if (cfg.enabled && cfg.host) { if (!cli || was.host !== cfg.host) start(snap); } else stop(); }
  function tick(snap) { publishState(snap); }
  function status() { return { enabled: !!cfg.enabled, connected: !!(cli && cli.isConnected()), host: cfg.host || '' }; }

  return { configure, tick, status, stop };
}

module.exports = { createHomeAssistant, mqttConnect };
