const { SerialPort } = require('serialport');

function resolvePositiveInt(rawValue, fallback) {
  const parsed = Number.parseInt(rawValue, 10);
  if (Number.isFinite(parsed) && parsed > 0) {
    return parsed;
  }
  return fallback;
}

const config = {
  path: process.env.RFID_PORT || '',
  baudRate: resolvePositiveInt(process.env.RFID_BAUD_RATE, 115200),
  interval: Math.max(resolvePositiveInt(process.env.RFID_INTERVAL, 500), 250),
  reconnectInterval: Math.max(
    resolvePositiveInt(process.env.RFID_RECONNECT_INTERVAL, 3000),
    1000
  ),
  debug: process.env.RFID_DEBUG === '1',
};

let port = null;
let pollTimer = null;
let reconnectTimer = null;
let flushTagsTimer = null;
let buffer = Buffer.alloc(0);

// Tags vistos recientemente: tagId -> { tag, rssi, lastSeen }
const tagsSeen = new Map();
const TAG_TTL_MS = 2000;
const FLUSH_TAGS_INTERVAL_MS = 400;

function getCurrentTagsList() {
  const now = Date.now();
  const list = [];
  for (const [id, entry] of tagsSeen.entries()) {
    if (now - entry.lastSeen <= TAG_TTL_MS) {
      list.push({ tag: entry.tag, rssi: entry.rssi, lastSeen: entry.lastSeen });
    }
  }
  list.sort((a, b) => (b.lastSeen || 0) - (a.lastSeen || 0));
  return list;
}

/** Devuelve la etiqueta con mayor intensidad (RSSI) de la lista; si no hay RSSI, la más reciente */
function getTagWithMaxRssi(list) {
  if (!list || list.length === 0) return null;
  let best = list[0];
  for (let i = 1; i < list.length; i++) {
    const a = best.rssi != null ? best.rssi : -1;
    const b = list[i].rssi != null ? list[i].rssi : -1;
    if (b > a) best = list[i];
  }
  return best;
}

function flushTagsState() {
  const list = getCurrentTagsList();
  const primary = getTagWithMaxRssi(list);
  const state = {
    tags: list,
    updatedAt: Date.now(),
    ready: true,
    error: null,
  };
  if (primary) {
    state.tag = primary.tag;
    if (primary.rssi != null) state.rssi = primary.rssi;
  }
  updateState(state);
}

const INVENTORY_COMMAND = Buffer.from([0xbb, 0x00, 0x22, 0x00, 0x00, 0x22, 0x7e]);

function sendMessage(type, payload) {
  if (typeof process.send === 'function') {
    process.send({ type, payload });
  }
}

function sendLog(message) {
  if (config.debug) {
    sendMessage('log', { message });
  }
}

function updateState(patch) {
  sendMessage('update', patch);
}

function scheduleReconnect() {
  if (reconnectTimer) {
    return;
  }
  reconnectTimer = setTimeout(() => {
    reconnectTimer = null;
    start();
  }, config.reconnectInterval);
}

function clearTimers() {
  if (pollTimer) {
    clearInterval(pollTimer);
    pollTimer = null;
  }
  if (reconnectTimer) {
    clearTimeout(reconnectTimer);
    reconnectTimer = null;
  }
  if (flushTagsTimer) {
    clearInterval(flushTagsTimer);
    flushTagsTimer = null;
  }
}

function handleMessageFrame(frame) {
  if (!frame || frame.length < 7) {
    return;
  }
  const command = frame[2];
  if (command !== 0x22 || frame.length <= 10) {
    return;
  }
  const start = 7;
  const end = frame.length - 3;
  if (end <= start) {
    return;
  }
  const epcBuffer = frame.slice(start, end);
  if (!epcBuffer.length) {
    return;
  }

  const tag = epcBuffer.toString('hex').toUpperCase();
  if (!tag) {
    return;
  }

  // RSSI (intensidad de señal) en byte 5 según protocolo 0xBB/0x22
  const rssi = frame.length > 6 ? frame[5] : null;

  const now = Date.now();
  tagsSeen.set(tag, { tag, rssi, lastSeen: now });

  const list = getCurrentTagsList();
  const primary = getTagWithMaxRssi(list);
  const state = {
    tag: primary ? primary.tag : tag,
    tags: list,
    updatedAt: now,
    ready: true,
    error: null,
  };
  if (primary && primary.rssi != null) state.rssi = primary.rssi;
  else if (rssi !== null && rssi !== undefined) state.rssi = rssi;
  updateState(state);
}

function processBuffer() {
  while (buffer.length > 0) {
    const start = buffer.indexOf(0xbb);
    if (start < 0) {
      buffer = Buffer.alloc(0);
      return;
    }
    if (start > 0) {
      buffer = buffer.slice(start);
    }

    const end = buffer.indexOf(0x7e, 1);
    if (end < 0) {
      return;
    }

    const frame = buffer.slice(0, end + 1);
    buffer = buffer.slice(end + 1);

    if (config.debug) {
      sendLog(`Frame: ${frame.toString('hex').toUpperCase()}`);
    }

    handleMessageFrame(frame);
  }
}

function sendCommand(command) {
  if (port && port.isOpen) {
    port.write(command, (error) => {
      if (error) {
        sendMessage('error', { error: error.message || String(error) });
      }
    });
  }
}

function start() {
  if (port) {
    return;
  }

  try {
    port = new SerialPort({
      path: config.path,
      baudRate: config.baudRate,
      dataBits: 8,
      parity: 'none',
      stopBits: 1,
      autoOpen: false,
    });
  } catch (error) {
    sendMessage('error', { error: error.message || String(error) });
    updateState({ ready: false, error: error.message || String(error) });
    scheduleReconnect();
    return;
  }

  port.on('open', () => {
    buffer = Buffer.alloc(0);
    tagsSeen.clear();
    clearTimers();
    updateState({ ready: true, error: null, port: config.path, tags: [] });

    sendCommand(INVENTORY_COMMAND);
    pollTimer = setInterval(() => {
      sendCommand(INVENTORY_COMMAND);
    }, config.interval);
    flushTagsTimer = setInterval(flushTagsState, FLUSH_TAGS_INTERVAL_MS);
  });

  port.on('data', (chunk) => {
    if (!chunk || !chunk.length) {
      return;
    }
    buffer = Buffer.concat([buffer, chunk]);
    processBuffer();
  });

  port.on('error', (error) => {
    sendMessage('error', { error: error.message || String(error) });
    updateState({ ready: false, error: error.message || String(error) });
    if (port && port.isOpen) {
      port.close();
    }
  });

  port.on('close', () => {
    clearTimers();
    tagsSeen.clear();
    updateState({ ready: false, error: null, tags: [] });
    port = null;
    scheduleReconnect();
  });

  port.open((error) => {
    if (error) {
      sendMessage('error', { error: error.message || String(error) });
      updateState({ ready: false, error: error.message || String(error) });
      port.removeAllListeners();
      port = null;
      scheduleReconnect();
    }
  });
}

function shutdown() {
  clearTimers();
  const currentPort = port;
  port = null;
  if (currentPort) {
    try {
      currentPort.removeAllListeners();
      if (currentPort.isOpen) {
        currentPort.close();
      }
    } catch (error) {
      sendMessage('error', { error: error.message || String(error) });
    }
  }
}

start();

process.on('SIGINT', () => {
  shutdown();
  process.exit(0);
});

process.on('SIGTERM', () => {
  shutdown();
  process.exit(0);
});

process.on('disconnect', () => {
  shutdown();
  process.exit(0);
});
