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 buffer = Buffer.alloc(0);

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;
  }
}

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;
  }

  updateState({
    tag,
    updatedAt: Date.now(),
    ready: true,
    error: null,
  });
}

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);
    clearTimers();
    updateState({ ready: true, error: null, port: config.path });

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

  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();
    updateState({ ready: false, error: null });
    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);
});
