/**
 * scale-service.js — 电子秤串口通信服务
 *
 * 通过 Node.js serialport 连接电子秤，解析多品牌协议，
 * 实时推送重量数据和连接状态。
 *
 * 支持协议：
 *  - dahua / tm : 大华系列（含 TM-F / TM-A 台秤、计价秤，连续 ASCII 输出）
 *  - dingjian   : 顶尖（CAS 类）W 前缀协议
 *  - toledo     : 托利多（METTLER TOLEDO）连续/指令协议
 *  - yaohua     : 耀华（YaoHua）紧凑 ASCII 协议
 *  - cashreg    : 主流收款机自带秤（大华芯 / 顶尖芯 / 紧凑 ASCII 组合嗅探）
 *  - auto       : 自动嗅探全部协议
 */

const { SerialPort } = require('serialport');
const { ReadlineParser } = require('@serialport/parser-readline');
const EventEmitter = require('events');

class ScaleService extends EventEmitter {
  constructor() {
    super();
    this.port = null;
    this.parser = null;
    this._connected = false;
    this._protocol = 'auto';
    this._buffer = '';
    this._currentWeight = { weight: 0, stable: false, stableWeight: 0, unit: 'kg' };
    // 连续稳定判定状态
    this._lastRawWeight = null;
    this._stableCount = 0;
  }

  get connected() { return this._connected; }
  get currentWeight() { return { ...this._currentWeight }; }

  /**
   * 列出可用串口
   */
  static async listPorts() {
    try {
      const ports = await SerialPort.list();
      return ports.map(p => ({
        path: p.path,
        manufacturer: p.manufacturer || p.serialNumber || p.pnpId || 'Unknown',
        pnpId: p.pnpId,
        vendorId: p.vendorId,
        productId: p.productId,
      }));
    } catch (err) {
      return [];
    }
  }

  /**
   * 连接电子秤
   * @param {Object} options
   * @param {string} options.path - COM口路径 (e.g. 'COM3')
   * @param {number} options.baudRate - 波特率 (default 9600)
   * @param {number} options.dataBits - 数据位 (default 8)
   * @param {number} options.stopBits - 停止位 (default 1)
   * @param {string} options.parity - 校验位 (default 'none')
   * @param {string} options.protocol - 协议: 'auto'|'dahua'|'tm'|'dingjian'|'toledo'|'yaohua'|'cashreg'
   */
  async connect(options = {}) {
    if (this._connected) await this.disconnect();

    const {
      path,
      baudRate = 9600,
      dataBits = 8,
      stopBits = 1,
      parity = 'none',
      protocol = 'auto',
    } = options;

    if (!path) throw new Error('COM port path is required');

    return new Promise((resolve, reject) => {
      try {
        this.port = new SerialPort({
          path,
          baudRate,
          dataBits,
          stopBits,
          parity,
          autoOpen: false,
        });

        this.port.open((err) => {
          if (err) {
            this._connected = false;
            this.emit('status', { status: 'error', message: err.message });
            reject(err);
            return;
          }

          this._protocol = protocol;
          this._connected = true;
          this._lastRawWeight = null;
          this._stableCount = 0;

          // 使用 Readline parser（按行解析）
          const delimiter = '\r\n';
          this.parser = this.port.pipe(new ReadlineParser({ delimiter, includeDelimiter: false }));

          this.parser.on('data', (line) => this._parseLine(line.toString().trim()));

          this.port.on('close', () => {
            this._connected = false;
            this.emit('status', { status: 'disconnected', message: 'Port closed' });
          });

          this.port.on('error', (e) => {
            this._connected = false;
            this.emit('status', { status: 'error', message: e.message });
          });

          this.emit('status', { status: 'connected', message: `Connected to ${path}` });
          resolve({ success: true });
        });
      } catch (e) {
        reject(e);
      }
    });
  }

  /**
   * 断开连接
   */
  async disconnect() {
    return new Promise((resolve) => {
      if (this.parser) {
        this.parser.removeAllListeners();
        this.parser = null;
      }
      if (this.port && this.port.isOpen) {
        this.port.close((err) => {
          this._connected = false;
          this.emit('status', { status: 'disconnected', message: err ? err.message : 'Disconnected' });
          resolve();
        });
      } else {
        this._connected = false;
        resolve();
      }
    });
  }

  /**
   * 获取当前状态
   */
  getStatus() {
    return {
      connected: this._connected,
      weight: this._currentWeight,
    };
  }

  /**
   * 解析一行数据，并统一做稳定性判定
   */
  _parseLine(line) {
    if (!line) return;

    const result = this._tryParse(line);
    if (result === null) return;

    // 稳定性判定策略：
    //  - 解析器显式返回 true / false → 直接采用（如大华 GS/NT 标志）
    //  - 解析器返回 null（无显式稳定标志）→ 连续 2 帧读数相同视为稳定
    if (result.stable === null) {
      if (result.weight === this._lastRawWeight) {
        this._stableCount += 1;
      } else {
        this._stableCount = 0;
        this._lastRawWeight = result.weight;
      }
      const stable = this._stableCount >= 1; // 第二帧起视为稳定
      result.stable = stable;
      result.stableWeight = stable ? result.weight : 0;
    }

    this._currentWeight = result;
    this._emitWeight();
  }

  /**
   * 按当前协议选择解析器
   */
  _tryParse(line) {
    if (this._protocol === 'dahua' || this._protocol === 'tm') return this._parseDahua(line);
    if (this._protocol === 'dingjian') return this._parseDingjian(line);
    if (this._protocol === 'toledo') return this._parseToledo(line);
    if (this._protocol === 'yaohua') return this._parseYaohua(line);
    if (this._protocol === 'cashreg') return this._parseCashReg(line);
    // auto: 依次嗅探（dahua 仅吃 W 前缀 / GS-NT 标志，不抢纯数字）
    return this._parseDahua(line)
      || this._parseDingjian(line)
      || this._parseToledo(line)
      || this._parseYaohua(line)
      || this._parseCashReg(line)
      || this._parseGeneric(line);
  }

  /**
   * 大华系列（TM-F / TM-A / TM 台秤 / 计价秤）多格式兼容：
   *   1) 稳定标志格式:  GS,+00012.345,kg  /  NT,+00012.345,kg  （可省略 ST, 前缀）
   *   2) W 前缀连续输出: W +00012.345 kg  /  W+00012.345kg  /  W -0001.234kg
   *   3) 紧凑/定宽数字:  +00012.345kg  /  +00012.345 kg  （无前缀，交给连续判定）
   * 单位支持 kg / g。
   */
  _parseDahua(line) {
    // 1) GS / NT 稳定标志（显式 stable）
    let m = line.match(/^(?:ST,)?\s*(GS|NT)\s*,\s*([+\-]?\d+\.?\d*)\s*,?\s*(kg|g)?/i);
    if (m) {
      const stable = m[1].toUpperCase() === 'GS';
      const weight = parseFloat(m[2]);
      const unit = (m[3] || 'kg').toLowerCase();
      if (!isNaN(weight)) return { weight, stable, stableWeight: stable ? weight : 0, unit };
    }
    // 2) W 前缀连续输出（无显式稳定标志）
    m = line.match(/^W\s*([+\-]?\d+\.?\d*)\s*(kg|g)?/i);
    if (m) {
      const weight = parseFloat(m[1]);
      const unit = (m[2] || 'kg').toLowerCase();
      if (!isNaN(weight)) return { weight, stable: null, stableWeight: 0, unit };
    }
    return null;
  }

  /**
   * 顶尖（CAS 类）协议: "W+0001.500kg" / "W +0001.500 kg"
   * 无显式稳定标志，交连续判定。
   */
  _parseDingjian(line) {
    const m = line.match(/^W\s*([+\-]?\d+\.?\d*)\s*(kg|g)?/i);
    if (!m) return null;
    const weight = parseFloat(m[1]);
    const unit = (m[2] || 'kg').toLowerCase();
    if (isNaN(weight)) return null;
    return { weight, stable: null, stableWeight: 0, unit };
  }

  /**
   * 托利多（METTLER TOLEDO）协议: "±N.NNN kg"（已去 STX / CR / LF）
   * 无显式稳定标志，交连续判定。
   */
  _parseToledo(line) {
    const m = line.match(/^([+\-]?\d{1,2}\.?\d{0,5})\s*(kg|g)?/);
    if (!m) return null;
    const weight = parseFloat(m[1]);
    const unit = (m[2] || 'kg').toLowerCase();
    if (isNaN(weight)) return null;
    return { weight, stable: null, stableWeight: 0, unit };
  }

  /**
   * 耀华（YaoHua）紧凑 ASCII 协议: "±N.NNNkg" / "N.NNNkg"，部分型号带帧头(SOI/W/=)
   * 无显式稳定标志，交连续判定。
   */
  _parseYaohua(line) {
    const cleaned = line.replace(/^(SOI|=|W)\s*/i, '').trim();
    const m = cleaned.match(/([+\-]?\d+\.\d+)\s*(kg|g)?/);
    if (!m) return null;
    const weight = parseFloat(m[1]);
    const unit = (m[2] || 'kg').toLowerCase();
    if (isNaN(weight) || weight > 99999) return null;
    return { weight, stable: null, stableWeight: 0, unit };
  }

  /**
   * 收款机自带秤：主流机型内部多为大华芯 / 顶尖芯 / 紧凑 ASCII 称重模块。
   * 依次嗅探大华芯、顶尖芯、紧凑 ASCII，覆盖绝大多数收款机内置秤。
   */
  _parseCashReg(line) {
    // 大华芯
    const d = this._parseDahua(line);
    if (d) return d;
    // 顶尖芯
    const dj = this._parseDingjian(line);
    if (dj) return dj;
    // 紧凑 ASCII（内置称重模块常见）
    const m = line.match(/([+\-]?\d+\.\d+)\s*(kg|g)?/);
    if (m) {
      const weight = parseFloat(m[1]);
      const unit = (m[2] || 'kg').toLowerCase();
      if (!isNaN(weight) && weight < 100000) return { weight, stable: null, stableWeight: 0, unit };
    }
    return null;
  }

  /**
   * 通用协议：提取首个带小数的浮点数（异常值过滤）
   */
  _parseGeneric(line) {
    const m = line.match(/([+\-]?\d+\.\d+)/);
    if (!m) return null;
    const weight = parseFloat(m[1]);
    if (isNaN(weight) || weight > 99999) return null; // 异常值过滤
    return { weight, stable: null, stableWeight: 0, unit: 'kg' };
  }

  _emitWeight() {
    this.emit('weight', { ...this._currentWeight });
  }
}

module.exports = ScaleService;
