/**
 * [顺丰] 顺丰面单打印实例
 * @typedef {Object} sf_express_instance
 * @property {string} version - 实例的版本号。
 * @property {sf_express_instance_print_function} print - 打印信息的方法。
 * @property {sf_express_instance_devices_function} devices - 获取打印机设备的方法。
 */

/**
 * [顺丰] 打印信息的方法的类型定义。
 * @callback sf_express_instance_print_function
 * @param {object} data - 要打印的数据。
 * @param {Object} [options] - 打印选项（可选）。
 * @param {'PRINT' | 'PREVIEW'} [options.lodopFn="PRINT"] - 打印类型。
 * @param {boolean} [options.allPreview=false] - 预览全部面单
 * @returns {Promise<void>}
 */

/**
 * [顺丰] 打印设备的方法的类型定义。
 * @callback sf_express_instance_devices_function
 * @param {string} [name] - 打印机名称前缀。
 * @returns {Promise<{name: string; index: number}[]>}
 */


import {withResolvers} from '@/tools/object';

/**
 * 获取顺丰打印实例
 * @param options
 * @param {string?} options.appid 合作伙伴编码（即顾客编码）
 * @param {string?} options.appname 打印机设备名称（即设备名称）
 * @param {'pro' | 'sbox'?} options.env 环境
 * @returns {Promise<sf_express_instance>}
 */
let sf_express = async function (options = {}) {
  const module = await import('@/libs/print/SCPPrint-2.7.1.js');

  const {promise: load, resolve, reject} = withResolvers();

  const instance = new module.default({
    partnerID: options.appid || process.env.VUE_APP_SF_EXPRESS_APPID,
    env: options.env || process.env.VUE_APP_SF_EXPRESS_ENV || (process.env.NODE_ENV === 'development' ? 'sbox' : 'pro'),
    notips: false,
    callback(result) {
      if (result.code === 1) resolve(instance);
      else reject({code: result.code, message: result.msg});
    },
  });

  try {
    const instance = await load;
    sf_express = () => Promise.resolve(instance);
    return instance;
  } catch (e) {}
  return instance;
};

/**
 *
 * @type {sf_express_instance_print_function}
 */
export async function sf_express_print(data, options = {}, device) {
  const instance = await sf_express(options);

  if (device == null) {
    const {promise: loadDevices, resolve, reject} = withResolvers();

    instance['getPrinters']((result) => {
      if (result.code === 1) {
        const devices = Array.isArray(result.printers) ? result.printers : [];
        resolve(devices);
      } else reject({message: result.msg, code: result.code});
    });

    const devices = await loadDevices;

    device = devices.find(device => device.name.startsWith(process.env.VUE_APP_SF_EXPRESS_APPNAME));
  }

  if (device) instance['setPrinter'](device.index);
  return new Promise((resolve, reject) => {
    instance.print(data, ({code, msg}) => {
      if (code === 1) resolve()
      else reject({code, message: msg});
    });
  });
}

let c_lodop = async function (options = {}) {
  const module = await import('@/libs/print/CLodop.js');

  const instance = await module.default(options);
  c_lodop = () => Promise.resolve(instance);

  return instance;
};

export async function getDevices() {
  const LODOP = await c_lodop();
  return Array.from({length: LODOP['GET_PRINTER_COUNT']()}, (_, i) => {
    return {
      name: LODOP['GET_PRINTER_NAME'](`${i}`),
      driver: LODOP['GET_PRINTER_NAME'](`${i}:DriverName`),
      index: i,
      papers: LODOP['GET_PAGESIZES_LIST'](i, '\n').split('\n'),
      size: +LODOP['GET_PRINTER_NAME'](`${i}:PaperSize`),
      width: +LODOP['GET_PRINTER_NAME'](`${i}:PaperWidth`),
      height: +LODOP['GET_PRINTER_NAME'](`${i}:PaperLength`),
      dpi: +LODOP['GET_PRINTER_NAME'](`${i}:PrintQuality`),
      form: LODOP['GET_PRINTER_NAME'](`${i}:FormName`),
    };
  });
}

export async function getDevice(...priority) {
  const devices = await getDevices();
  let device;
  for (let item of priority) {
    let command = [];
    if (typeof item === 'string') {
      const regex = /([^:;]+):+([^;]*)/g;
      let match;
      while ((match = regex.exec(item))) {
        const key = match[1].trim();
        const value = match[2].trim();
        if (key && value) command.push([key, value]);
      }
    } else {
      command = Object.entries(item);
    }
    device = devices.find(device => command.every(([key, value]) => {
      if (key === 'paper') return device['papers'].includes(value);
      if (key === 'width_mm' || key === 'height_mm') value *= 10; /* 传入 mm，比较的是 0.1mm */
      return device[key] === value;
    }));
    if (device) break;
  }
  return device;
}

export default function (options = {}) {
  return c_lodop(options);
}

