// 网口打印机管理（80mm 终极美化版 · 无多余间距最终版）
import net from 'net'
import iconv from 'iconv-lite'

// ======================== 全局工具函数 ========================
const getPrintWidth = (str) => {
    let width = 0
    const s = String(str || '')
    for (let i = 0; i < s.length; i++) {
        const char = s.charAt(i)
        if (/[\u4e00-\u9fa5\u3000-\u303f\uff00-\uffef]/.test(char)) {
            width += 2
        } else {
            width += 1
        }
    }
    return width
}

const padLeftAlign = (str, targetWidth) => {
    const s = String(str || '')
    const currentWidth = getPrintWidth(s)
    const spaceNum = Math.max(0, targetWidth - currentWidth)
    return s + ' '.repeat(spaceNum)
}

const padRightAlign = (str, targetWidth) => {
    const s = String(str || '')
    const currentWidth = getPrintWidth(s)
    const spaceNum = Math.max(0, targetWidth - currentWidth)
    return ' '.repeat(spaceNum) + s
}

const truncateByPrintWidth = (str, maxWidth) => {
    const s = String(str || '')
    let width = 0
    let result = ''
    for (const char of s) {
        const charWidth = /[\u4e00-\u9fa5\u3000-\u303f\uff00-\uffef]/.test(char) ? 2 : 1
        if (width + charWidth > maxWidth) break
        result += char
        width += charWidth
    }
    return result
}

const centerAlign = (str, lineWidth = 48) => {
    const s = String(str || '').trim(); // 去除首尾空格，避免干扰
    const currentWidth = getPrintWidth(s);
    if (currentWidth >= lineWidth) return s; // 内容超宽时直接返回，不居中

    const totalPad = lineWidth - currentWidth;
    const leftPad = Math.floor(totalPad / 2);
    const rightPad = totalPad - leftPad; // 避免奇数宽度时总长度超行宽

    // 用空格填充左右，保证总宽度严格等于 lineWidth
    return ' '.repeat(leftPad) + s + ' '.repeat(rightPad);
};

const createDivider = (lineWidth = 48, type = 'full') => {
    const fullChar = '═'
    const thinChar = '─'
    const char = type === 'full' ? fullChar : thinChar
    return char.repeat(Math.ceil(lineWidth / 2)).substring(0, lineWidth)
}

const setupPrinter = (socket, options = {}) => {
    socket.write(Buffer.from([0x1B, 0x40]))
    if (options.fontSize) {
        socket.write(Buffer.from([0x1B, 0x21, options.fontSize]))
    }
    if (options.bold) {
        socket.write(Buffer.from([0x1B, 0x45, options.bold ? 0x01 : 0x00]))
    }
}

// 用餐模式 堂食 dine-in 外带 take-out 外卖 take-away
const dineModeFilter = t => {
    if (t === 'dine-in') return '堂食'
    if (t === 'take-out') return '外带'
    if (t === 'take-away') return '外卖'
    return t
}

// 支付类型 主扫 main_scan
// 被扫 back_scan
// 微信小程序 wechat_mini
// 支付宝小程序 alipay_mini
// 会员支付 vip_pay
// 现金支付 cash_pay
// 挂账支付 credit_pay
const payTypeFilter = t => {
    if (t === 'main_scan') return '主扫'
    if (t === 'back_scan') return '被扫'
    if (t === 'wechat_mini') return '微信小程序'
    if (t === 'alipay_mini') return '支付宝小程序'
    if (t === 'vip_pay') return '余额支付'
    if (t === 'cash_pay') return '现金支付'
    if (t === 'credit_pay') return '挂账支付'
    return t
}

// ======================== 打印结算小票 ========================
export function printReceipt(printerIp, order) {
    // console.log('Printing receipt...', JSON.stringify(order));
    return new Promise((resolve) => {
        const socket = new net.Socket()
        const lineWidth = 48
        socket.setTimeout(8000)

        const formatAmount = (amount) => Number(amount || 0).toFixed(2)

        socket.connect(9100, printerIp, () => {
            try {
                setupPrinter(socket)

                // ======================== 标题区域（彻底修复居中问题！） ========================
                const title1 = order.shop_name || '';
                let title2 = `结算单 #${order.orderInfo.orderNum}`
                if (order.isBefore) {
                    title2 = `${order.isBefore ? '预' : ''}结算单 #${order.orderInfo.orderNum}`;
                }
                if (order.isGuest) {
                    title2 = `客看单 #${order.orderInfo.orderNum}`;
                }

                // 核心修复：先写入空行清空打印机缓冲区，避免残留字符干扰居中起始位置
                socket.write(iconv.encode('\n', 'gbk'));

                // 步骤1：重置打印机格式（避免残留格式影响）
                socket.write(Buffer.from([0x1B, 0x40]));
                // 步骤2：居中对齐标题
                socket.write(Buffer.from([0x1B, 0x61, 0x01])); // 1 = center
                // 步骤3：设置标题1的格式（加粗+大号字体）
                socket.write(Buffer.from([0x1B, 0x21, 0x11])); // 字号放大（0x11 是常用放大值）
                socket.write(Buffer.from([0x1B, 0x45, 0x01])); // 加粗
                socket.write(iconv.encode(title1 + '\n', 'gbk'));

                // 步骤4：恢复格式，打印标题2（常规字体）
                socket.write(Buffer.from([0x1B, 0x21, 0x00])); // 恢复默认字号
                socket.write(Buffer.from([0x1B, 0x45, 0x00])); // 取消加粗
                socket.write(iconv.encode(title2 + '\n\n', 'gbk'));
                socket.write(Buffer.from([0x1B, 0x61, 0x00])); // 0 = left

                // ======================== 订单信息（修复显示+换行问题） ========================
                const tableLine = padLeftAlign('桌台号：', 8) + padLeftAlign(order.orderInfo.tableName || '无', 14) + '\n'
                socket.write(Buffer.from([0x1B, 0x21, 0x00])); // 正常字号
                socket.write(Buffer.from([0x1B, 0x45, 0x01])); // 加粗
                socket.write(iconv.encode(tableLine, 'gbk'));
                socket.write(Buffer.from([0x1B, 0x45, 0x00])); // 取消加粗

                let orderInfo = ''

                // 修复：打印机-用餐模式 + 就餐人数 展示逻辑
                const dineModeText = dineModeFilter(order.orderInfo.dineMode)
                // 1. 拼接打印机+用餐模式文本，空值兜底
                const printerDineText = `${order.printerName || '未知打印机'}-${dineModeText || '未知模式'}`
                // 2. 截断超长文本（左侧分配20宽度，保证不超限）
                const truncatedPrinterDine = truncateByPrintWidth(printerDineText, 20)
                // 3. 左侧左对齐填充20宽度（保证占满分配空间，不浪费空白）
                const leftPart = padLeftAlign(truncatedPrinterDine, 20)
                // 4. 右侧就餐人数：空值兜底为“无”，右对齐填充28宽度（20+28=48，刚好行宽）
                const seatNumText = order.orderInfo.seatNum || '0'
                const rightPart = padRightAlign(`${seatNumText}人`, 28) // 加标签更清晰，也可只显示数字

                orderInfo += leftPart + rightPart + '\n'
                if (!order.isBefore && !order.isGuest) {
                    orderInfo += padLeftAlign('结账时间：', 10) + padLeftAlign(order.orderInfo.paidTime || '-', 38) + '\n'
                }
                orderInfo += createDivider(lineWidth, 'thin') + '\n'

                // ======================== 商品表格 ========================
                orderInfo += padLeftAlign('品名', 18)
                orderInfo += padLeftAlign('单价', 8)
                orderInfo += padLeftAlign('数量', 6)
                orderInfo += padRightAlign('小计', 16) + '\n'
                orderInfo += createDivider(lineWidth, 'thin') + '\n'

                // 处理商品列表（套餐主项显示金额，子项不显示金额 + 规格换行）
                order.carts.forEach(item => {
                    if (item.number > 0) {
                        // 套餐商品逻辑
                        if (item.proGroupInfo) {
                            // 套餐主项：正常显示 名称、单价、数量、小计
                            const packageName = truncateByPrintWidth(item.name || '未知套餐', 18)
                            orderInfo += padLeftAlign(packageName, 18)
                            orderInfo += padLeftAlign(formatAmount(item.salePrice), 8)
                            orderInfo += padLeftAlign(item.number || 1, 6)
                            orderInfo += padRightAlign(formatAmount(item.totalAmount), 16) + '\n'

                            // 套餐子项：只显示名称+规格，单价、小计全部为空
                            const proGroupInfo = item.proGroupInfo || []
                            proGroupInfo.forEach(subItem => {
                                const subName = truncateByPrintWidth(`>${subItem.proName || '未知商品'}`, 18)
                                orderInfo += padLeftAlign(subName, 18)
                                orderInfo += padLeftAlign('', 8) // 子项单价空
                                orderInfo += padLeftAlign(subItem.number || 1, 6)
                                orderInfo += padRightAlign('', 16) + '\n' // 子项小计空

                                // 子项规格换行显示
                                if (subItem.skuName) {
                                    const skuText = truncateByPrintWidth(`  规格：${subItem.skuName}`, 18)
                                    orderInfo += padLeftAlign(skuText, 18)
                                    orderInfo += padLeftAlign('', 8)
                                    orderInfo += padLeftAlign('', 6)
                                    orderInfo += padRightAlign('', 16) + '\n'
                                }
                            })
                        } else {
                            // 普通商品：正常显示 + 规格换行
                            let productName = truncateByPrintWidth(item.name || '未知商品', 18)
                            orderInfo += padLeftAlign(productName, 18)
                            orderInfo += padLeftAlign(item.salePrice ? formatAmount(item.salePrice) : '', 8)
                            orderInfo += padLeftAlign(item.number || 1, 6)
                            orderInfo += padRightAlign(formatAmount(item.totalAmount), 16) + '\n'

                            // 规格单独换行
                            if (item.skuName) {
                                const skuLine = truncateByPrintWidth(`  规格：${item.skuName}`, 18)
                                orderInfo += padLeftAlign(skuLine, 18)
                                orderInfo += padLeftAlign('', 8)
                                orderInfo += padLeftAlign('', 6)
                                orderInfo += padRightAlign('', 16) + '\n'
                            }
                        }
                    }
                })

                orderInfo += createDivider(lineWidth, 'thin') + '\n'
                orderInfo += padLeftAlign('原价', 10) + padRightAlign(formatAmount(order.originAmount), 38) + '\n'
                orderInfo += padLeftAlign('优惠金额', 10) + padRightAlign(`-${formatAmount(order.discountAllAmount)}`, 38) + '\n'
                orderInfo += padLeftAlign('备注：', 6) + padLeftAlign(order.remark || '无', 38) + '\n'
                orderInfo += createDivider(lineWidth, 'thin') + '\n'
                socket.write(iconv.encode(orderInfo, 'gbk'))

                // ======================== 付款信息 ========================
                if (order.isBefore) {
                    const payableLine = padLeftAlign('应付', 10) + padRightAlign(formatAmount(order.originAmount), 51) + '\n'
                    socket.write(Buffer.from([0x1B, 0x21, 0x11])); // 放大字号
                    socket.write(Buffer.from([0x1B, 0x45, 0x01])); // 加粗
                    socket.write(iconv.encode(payableLine, 'gbk'));
                    socket.write(Buffer.from([0x1B, 0x21, 0x00])); // 恢复默认字号
                    socket.write(Buffer.from([0x1B, 0x45, 0x00])); // 取消加粗
                    socket.write(iconv.encode(createDivider(lineWidth, 'thin') + '\n', 'gbk'));
                }

                if (!order.isBefore && !order.isGuest) {
                    const payableLine = padLeftAlign('应付', 10) + padRightAlign(formatAmount(order.orderInfo.payAmount), 51) + '\n'
                    socket.write(Buffer.from([0x1B, 0x21, 0x11])); // 放大字号
                    socket.write(Buffer.from([0x1B, 0x45, 0x01])); // 加粗
                    socket.write(iconv.encode(payableLine, 'gbk'));
                    socket.write(Buffer.from([0x1B, 0x21, 0x00])); // 恢复默认字号
                    socket.write(Buffer.from([0x1B, 0x45, 0x00])); // 取消加粗
                    socket.write(iconv.encode(createDivider(lineWidth, 'thin') + '\n', 'gbk'));
                    const paidLine = padLeftAlign('已付', 10) + padRightAlign(formatAmount(order.orderInfo.payAmount), 51) + '\n'
                    socket.write(Buffer.from([0x1B, 0x21, 0x11]));
                    socket.write(Buffer.from([0x1B, 0x45, 0x01]));
                    socket.write(iconv.encode(paidLine, 'gbk'));
                    socket.write(Buffer.from([0x1B, 0x21, 0x00]));
                    socket.write(Buffer.from([0x1B, 0x45, 0x00]));
                    socket.write(iconv.encode(createDivider(lineWidth, 'thin') + '\n', 'gbk'));
                }

                // ======================== 底部 ========================
                let bottom = ''
                if (!order.isBefore && !order.isGuest) {
                    bottom += padLeftAlign('支付方式：', 8) + padLeftAlign(payTypeFilter(order.orderInfo.payType) || '无', 38) + '\n'
                }
                bottom += padLeftAlign('操作员：', 8) + padLeftAlign(order.loginAccount || '无', 38) + '\n'
                bottom += padLeftAlign('打印时间：', 10) + padLeftAlign(new Date().toLocaleString(), 38) + '\n'
                bottom += padLeftAlign('订单号：', 8) + padLeftAlign(order.orderInfo.orderNo || '-', 38) + '\n'
                bottom += createDivider(lineWidth, 'full') + '\n'
                bottom += '\n\n'
                bottom += '\n\n'

                socket.write(iconv.encode(bottom, 'gbk'))
                socket.write(Buffer.from([0x1D, 0x56, 0x00]))

                setTimeout(() => {
                    socket.end()
                    resolve('success')
                }, 400)
            } catch (err) {
                console.error(err)
                socket.destroy()
                resolve('error')
            }
        })

        socket.on('error', () => resolve('error'))
        socket.on('timeout', () => resolve('timeout'))
        socket.on('close', () => { })
    })
}

// ======================== 退款小票 ========================
export function printRefund(printerIp, order) {
    console.log(JSON.stringify(order));

    return new Promise((resolve) => {
        const socket = new net.Socket()
        const lineWidth = 48
        socket.setTimeout(8000)

        const formatAmount = (amount) => Number(amount || 0).toFixed(2)

        socket.connect(9100, printerIp, () => {
            try {
                setupPrinter(socket)

                // ======================== 标题区域（彻底修复居中问题！） ========================
                const title1 = order.shop_name || '';
                let title2 = '退款单';

                // 核心修复：先写入空行清空打印机缓冲区，避免残留字符干扰居中起始位置
                socket.write(iconv.encode('\n', 'gbk'));

                // 步骤1：重置打印机格式（避免残留格式影响）
                socket.write(Buffer.from([0x1B, 0x40]));
                // 步骤2：居中对齐标题
                socket.write(Buffer.from([0x1B, 0x61, 0x01])); // 1 = center
                // 步骤3：设置标题1的格式（加粗+大号字体）
                socket.write(Buffer.from([0x1B, 0x21, 0x11])); // 字号放大（0x11 是常用放大值）
                socket.write(Buffer.from([0x1B, 0x45, 0x01])); // 加粗
                socket.write(iconv.encode(title1 + '\n', 'gbk'));

                // 步骤4：恢复格式，打印标题2（常规字体）
                socket.write(Buffer.from([0x1B, 0x21, 0x00])); // 恢复默认字号
                socket.write(Buffer.from([0x1B, 0x45, 0x00])); // 取消加粗
                socket.write(iconv.encode(title2 + '\n\n', 'gbk'));
                socket.write(Buffer.from([0x1B, 0x61, 0x00])); // 0 = left

                // ======================== 订单信息（修复显示+换行问题） ========================
                const tableLine = padLeftAlign('桌台号：', 8) + padLeftAlign(order.orderInfo.tableName || '无', 14) + '\n'
                socket.write(Buffer.from([0x1B, 0x21, 0x00])); // 正常字号
                socket.write(Buffer.from([0x1B, 0x45, 0x01])); // 加粗
                socket.write(iconv.encode(tableLine, 'gbk'));
                socket.write(Buffer.from([0x1B, 0x45, 0x00])); // 取消加粗

                let orderInfo = ''

                // 修复：打印机-用餐模式 + 就餐人数 展示逻辑
                const dineModeText = dineModeFilter(order.orderInfo.dineMode)
                // 1. 拼接打印机+用餐模式文本，空值兜底
                const printerDineText = `${order.printerName || '未知打印机'}-${dineModeText || '未知模式'}`
                // 2. 截断超长文本（左侧分配20宽度，保证不超限）
                const truncatedPrinterDine = truncateByPrintWidth(printerDineText, 20)
                // 3. 左侧左对齐填充20宽度（保证占满分配空间，不浪费空白）
                const leftPart = padLeftAlign(truncatedPrinterDine, 20)
                // 4. 右侧就餐人数：空值兜底为“无”，右对齐填充28宽度（20+28=48，刚好行宽）
                const seatNumText = order.orderInfo.seatNum || '0'
                const rightPart = padRightAlign(`${seatNumText}人`, 28) // 加标签更清晰，也可只显示数字

                if (order.carts.length > 0) {
                    orderInfo += leftPart + rightPart + '\n'
                    orderInfo += createDivider(lineWidth, 'thin') + '\n'

                    // ======================== 商品表格 ========================
                    orderInfo += padLeftAlign('品名', 18)
                    orderInfo += padLeftAlign('单价', 8)
                    orderInfo += padLeftAlign('数量', 6)
                    orderInfo += padRightAlign('小计', 16) + '\n'
                    orderInfo += createDivider(lineWidth, 'thin') + '\n'

                    order.carts.forEach(item => {
                        const name = truncateByPrintWidth(item.name || '未知商品', 18)
                        const price = formatAmount(item.salePrice)
                        const num = item.number || 1
                        const total = formatAmount(item.totalAmount)
                        orderInfo += padLeftAlign(name, 18)
                        orderInfo += padLeftAlign(price, 8)
                        orderInfo += padLeftAlign(num, 6)
                        orderInfo += padRightAlign(total, 16) + '\n'
                    })

                    orderInfo += createDivider(lineWidth, 'thin') + '\n'
                    socket.write(iconv.encode(orderInfo, 'gbk'))
                }

                // ======================== 付款信息 ========================

                let payableLine = ''
                payableLine += padLeftAlign('退款总计', 10) + padRightAlign(formatAmount(order.amount), 51) + '\n'
                socket.write(Buffer.from([0x1B, 0x21, 0x11])); // 放大字号
                socket.write(Buffer.from([0x1B, 0x45, 0x01])); // 加粗
                socket.write(iconv.encode(payableLine, 'gbk'));
                socket.write(Buffer.from([0x1B, 0x21, 0x00])); // 恢复默认字号
                socket.write(Buffer.from([0x1B, 0x45, 0x00])); // 取消加粗

                let refundInfo = ''
                refundInfo += padLeftAlign('退款方式：', 10) + padLeftAlign(order.refundMethod || '无', 38) + '\n'
                refundInfo += padLeftAlign('退款原因：', 10) + padLeftAlign(order.remark || '无', 38) + '\n'
                socket.write(iconv.encode(refundInfo, 'gbk'));
                socket.write(iconv.encode(createDivider(lineWidth, 'thin') + '\n', 'gbk'));

                // ======================== 底部 ========================
                let bottom = ''
                bottom += padLeftAlign('操作员：', 8) + padLeftAlign(order.loginAccount || '无', 38) + '\n'
                bottom += padLeftAlign('打印时间：', 10) + padLeftAlign(new Date().toLocaleString(), 38) + '\n'
                bottom += padLeftAlign('原订单号：', 10) + padLeftAlign(order.orderInfo.orderNo || '-', 38) + '\n'
                bottom += createDivider(lineWidth, 'full') + '\n'
                bottom += '\n\n'
                bottom += '\n\n'

                socket.write(iconv.encode(bottom, 'gbk'))
                socket.write(Buffer.from([0x1D, 0x56, 0x00]))

                setTimeout(() => {
                    socket.end()
                    resolve('success')
                }, 400)
            } catch (err) {
                console.error(err)
                socket.destroy()
                resolve('error')
            }
        })

        socket.on('error', () => resolve('error'))
        socket.on('timeout', () => resolve('timeout'))
        socket.on('close', () => { })
    })
}

// ======================== 打印退菜单 ========================
export function printRefundDish(printerIp, order) {
    console.log(JSON.stringify(order));

    return new Promise((resolve) => {
        const socket = new net.Socket()
        const lineWidth = 48
        socket.setTimeout(8000)

        const formatAmount = (amount) => Number(amount || 0).toFixed(2)

        socket.connect(9100, printerIp, () => {
            try {
                setupPrinter(socket)

                // ======================== 标题区域（彻底修复居中问题！） ========================
                const title1 = order.shop_name || '';
                let title2 = '退菜单'

                // 核心修复：先写入空行清空打印机缓冲区，避免残留字符干扰居中起始位置
                socket.write(iconv.encode('\n', 'gbk'));

                // 步骤1：重置打印机格式（避免残留格式影响）
                socket.write(Buffer.from([0x1B, 0x40]));
                // 步骤2：居中对齐标题
                socket.write(Buffer.from([0x1B, 0x61, 0x01])); // 1 = center
                // 步骤3：设置标题1的格式（加粗+大号字体）
                socket.write(Buffer.from([0x1B, 0x21, 0x11])); // 字号放大（0x11 是常用放大值）
                socket.write(Buffer.from([0x1B, 0x45, 0x01])); // 加粗
                socket.write(iconv.encode(title1 + '\n', 'gbk'));

                // 步骤4：恢复格式，打印标题2（常规字体）
                socket.write(Buffer.from([0x1B, 0x21, 0x00])); // 恢复默认字号
                socket.write(Buffer.from([0x1B, 0x45, 0x00])); // 取消加粗
                socket.write(iconv.encode(title2 + '\n\n', 'gbk'));
                socket.write(Buffer.from([0x1B, 0x61, 0x00])); // 0 = left

                // ======================== 订单信息（修复显示+换行问题） ========================
                const tableLine = padLeftAlign('桌台号：', 8) + padLeftAlign(order.orderInfo.tableName || '无', 14) + '\n'
                socket.write(Buffer.from([0x1B, 0x21, 0x00])); // 正常字号
                socket.write(Buffer.from([0x1B, 0x45, 0x01])); // 加粗
                socket.write(iconv.encode(tableLine, 'gbk'));
                socket.write(Buffer.from([0x1B, 0x45, 0x00])); // 取消加粗

                let orderInfo = ''

                // 修复：打印机-用餐模式 + 就餐人数 展示逻辑
                const dineModeText = dineModeFilter(order.orderInfo.dineMode)
                // 1. 拼接打印机+用餐模式文本，空值兜底
                const printerDineText = `${order.printerName || '未知打印机'}-${dineModeText || '未知模式'}`
                // 2. 截断超长文本（左侧分配20宽度，保证不超限）
                const truncatedPrinterDine = truncateByPrintWidth(printerDineText, 20)
                // 3. 左侧左对齐填充20宽度（保证占满分配空间，不浪费空白）
                const leftPart = padLeftAlign(truncatedPrinterDine, 20)
                // 4. 右侧就餐人数：空值兜底为“无”，右对齐填充28宽度（20+28=48，刚好行宽）
                const seatNumText = order.orderInfo.seatNum || '0'
                const rightPart = padRightAlign(`${seatNumText}人`, 28) // 加标签更清晰，也可只显示数字

                if (order.carts.length > 0) {
                    orderInfo += leftPart + rightPart + '\n'
                    orderInfo += createDivider(lineWidth, 'thin') + '\n'

                    // ======================== 商品表格 ========================
                    orderInfo += padLeftAlign('品名', 18)
                    orderInfo += padLeftAlign('单价', 8)
                    orderInfo += padLeftAlign('数量', 6)
                    orderInfo += padRightAlign('小计', 16) + '\n'
                    orderInfo += createDivider(lineWidth, 'thin') + '\n'

                    order.carts.forEach(item => {
                        const name = truncateByPrintWidth(item.name || '未知商品', 18)
                        const price = formatAmount(item.salePrice)
                        const num = item.number || 1
                        const total = formatAmount(item.totalAmount)
                        orderInfo += padLeftAlign(name, 18)
                        orderInfo += padLeftAlign(price, 8)
                        orderInfo += padLeftAlign(num, 6)
                        orderInfo += padRightAlign(total, 16) + '\n'
                    })

                    orderInfo += createDivider(lineWidth, 'thin') + '\n'
                    socket.write(iconv.encode(orderInfo, 'gbk'))
                }

                // ======================== 付款信息 ========================
                // if (order.isGuest) {
                //     let payableLine = ''
                //     payableLine += padLeftAlign('退款总计', 10) + padRightAlign(formatAmount(order.amount), 51) + '\n'
                //     socket.write(Buffer.from([0x1B, 0x21, 0x11])); // 放大字号
                //     socket.write(Buffer.from([0x1B, 0x45, 0x01])); // 加粗
                //     socket.write(iconv.encode(payableLine, 'gbk'));
                //     socket.write(Buffer.from([0x1B, 0x21, 0x00])); // 恢复默认字号
                //     socket.write(Buffer.from([0x1B, 0x45, 0x00])); // 取消加粗
                // }
                // let refundInfo = ''
                // refundInfo += padLeftAlign('退款方式：', 10) + padLeftAlign(order.refundMethod || '无', 38) + '\n'
                // refundInfo += padLeftAlign('退款原因：', 10) + padLeftAlign(order.remark || '无', 38) + '\n'
                // socket.write(iconv.encode(refundInfo, 'gbk'));
                // socket.write(iconv.encode(createDivider(lineWidth, 'thin') + '\n', 'gbk'));

                // ======================== 底部 ========================
                let bottom = ''
                bottom += padLeftAlign('操作员：', 8) + padLeftAlign(order.loginAccount || '无', 38) + '\n'
                bottom += padLeftAlign('打印时间：', 10) + padLeftAlign(new Date().toLocaleString(), 38) + '\n'
                bottom += padLeftAlign('原订单号：', 10) + padLeftAlign(order.orderInfo.orderNo || '-', 38) + '\n'
                bottom += createDivider(lineWidth, 'full') + '\n'
                bottom += '\n\n'
                bottom += '\n\n'

                socket.write(iconv.encode(bottom, 'gbk'))
                socket.write(Buffer.from([0x1D, 0x56, 0x00]))

                setTimeout(() => {
                    socket.end()
                    resolve('success')
                }, 400)
            } catch (err) {
                console.error(err)
                socket.destroy()
                resolve('error')
            }
        })

        socket.on('error', () => resolve('error'))
        socket.on('timeout', () => resolve('timeout'))
        socket.on('close', () => { })
    })
}

// ======================== 打印交班小票（完整版 · 补齐分类/商品数据） ========================
export function printHandoverReceipt(printerIp, data) {
    return new Promise((resolve) => {
        const socket = new net.Socket()
        const lineWidth = 48
        socket.setTimeout(8000)

        const formatAmount = (v) => Number(v || 0).toFixed(2)

        socket.connect(9100, printerIp, () => {
            try {
                setupPrinter(socket)

                // ======================== 标题区域（统一结算小票的居中修复逻辑） ========================
                const title1 = data.shopName || '店铺';
                const title2 = '交班小票';

                // 核心修复：先写入空行清空打印机缓冲区，避免残留字符干扰居中起始位置
                socket.write(iconv.encode('\n', 'gbk'));

                // 步骤1：重置打印机格式（避免残留格式影响）
                socket.write(Buffer.from([0x1B, 0x40]));
                // 步骤2：居中对齐标题
                socket.write(Buffer.from([0x1B, 0x61, 0x01])); // 1 = center
                // 步骤3：设置标题1的格式（加粗+大号字体）
                socket.write(Buffer.from([0x1B, 0x21, 0x11])); // 字号放大（0x11 是常用放大值）
                socket.write(Buffer.from([0x1B, 0x45, 0x01])); // 加粗
                socket.write(iconv.encode(title1 + '\n', 'gbk'));

                // 步骤4：恢复格式，打印标题2（常规字体）
                socket.write(Buffer.from([0x1B, 0x21, 0x00])); // 恢复默认字号
                socket.write(Buffer.from([0x1B, 0x45, 0x00])); // 取消加粗
                socket.write(iconv.encode(title2 + '\n\n', 'gbk'));
                socket.write(Buffer.from([0x1B, 0x61, 0x00])); // 0 = left
                socket.write(iconv.encode(createDivider(lineWidth, 'thin') + '\n', 'gbk'));

                // ======================== 基础信息区域（拆分当班/交班时间） ========================
                let basicInfo = ''
                // 优化：拆分当班时间和交班时间，空值兜底
                basicInfo += padLeftAlign('交班时间：', 10) + padLeftAlign(data.handoverTime || '-', 36) + '\n'
                basicInfo += padLeftAlign('交班人：', 8) + padLeftAlign(data.staffName || '-', 36) + '\n'
                basicInfo += padLeftAlign('交班：', 6) + padLeftAlign(`${data.loginTime} - ${data.handoverTime}` || '-', 36) + '\n\n'

                // basicInfo += createDivider(lineWidth, 'thin') + '\n'

                // 收入明细 - 补充会员支付/充值字段
                basicInfo += padLeftAlign('当班营业总额：', 10) + padLeftAlign(formatAmount(data.turnover), 36) + '\n'
                basicInfo += padLeftAlign('实际收款的支付方式', 10) + '\n'
                basicInfo += padLeftAlign('现金', 10) + padRightAlign(formatAmount(data.cash), 36) + '\n'
                basicInfo += padLeftAlign('微信', 10) + padRightAlign(formatAmount(data.wechat), 36) + '\n'
                basicInfo += padLeftAlign('支付宝', 10) + padRightAlign(formatAmount(data.alipay), 36) + '\n'
                basicInfo += padLeftAlign('二维码收款', 10) + padRightAlign(formatAmount(data.selfScan), 36) + '\n'
                basicInfo += padLeftAlign('扫码收款', 10) + padRightAlign(formatAmount(data.barScan), 36) + '\n'
                basicInfo += padLeftAlign('充值', 10) + padRightAlign(formatAmount(data.recharge), 36) + '\n\n'
                basicInfo += padLeftAlign('非实际收款的支付方式', 10) + '\n'
                basicInfo += padLeftAlign('挂账', 10) + padRightAlign(formatAmount(data.owed), 36) + '\n'
                basicInfo += padLeftAlign('余额', 10) + padRightAlign(formatAmount(data.balance), 36) + '\n'
                basicInfo += createDivider(lineWidth, 'thin') + '\n'

                socket.write(Buffer.from([0x1B, 0x21, 0x00])); // 正常字号
                socket.write(iconv.encode(basicInfo, 'gbk'))

                let refundInfo = ''
                refundInfo += padLeftAlign('退款/退菜', 10) + '\n'
                refundInfo += padLeftAlign('退款金额', 10) + padRightAlign(formatAmount(data.refundAmount), 36) + '\n'
                refundInfo += padLeftAlign('退菜数量', 10) + padRightAlign(formatAmount(data.returnDishCount), 36) + '\n'
                refundInfo += createDivider(lineWidth, 'thin') + '\n'
                socket.write(iconv.encode(refundInfo, 'gbk'))

                let orderInfo = ''
                const orderLabel = '订单（数量/订单总额）'
                const orderValue = `${data.orderCount}/${formatAmount(data.orderTurnover)}`
                const orderLabelWidth = lineWidth - getPrintWidth(orderValue)
                orderInfo += padLeftAlign(orderLabel, orderLabelWidth) + orderValue + '\n\n'
                socket.write(iconv.encode(orderInfo, 'gbk'))

                // ======================== 分类数据区域 ========================
                let categoryInfo = ''
                if (data.categoryList && data.categoryList.length) {
                    categoryInfo += centerAlign('销售数据', lineWidth) + '\n'
                    categoryInfo += createDivider(lineWidth, 'thin') + '\n'
                    // 分类表头
                    categoryInfo += padLeftAlign('商品分类', 24) + padLeftAlign('数量', 12) + padRightAlign('总计', 12) + '\n'
                    // categoryInfo += createDivider(lineWidth, 'thin') + '\n'
                    // 分类数据行
                    data.categoryList.forEach(item => {
                        const catName = truncateByPrintWidth(item.categoryName || '未知分类', 24)
                        const num = item.num || 0
                        const amount = formatAmount(item.amount)
                        categoryInfo += padLeftAlign(catName, 24) + padLeftAlign(num, 12) + padRightAlign(amount, 12) + '\n'
                    })
                    categoryInfo += createDivider(lineWidth, 'thin') + '\n'
                }
                socket.write(iconv.encode(categoryInfo, 'gbk'))

                // ======================== 商品数据区域 ========================
                // let productInfo = ''
                // if (data.detailList && data.detailList.length) {
                //     productInfo += centerAlign('商品数据', lineWidth) + '\n'
                //     productInfo += createDivider(lineWidth, 'thin') + '\n'
                //     // 商品表头
                //     productInfo += padLeftAlign('商品', 36) + padRightAlign('数量', 12) + '\n'
                //     // productInfo += createDivider(lineWidth, 'thin') + '\n'
                //     // 商品数据行
                //     data.detailList.forEach(item => {
                //         const prodName = truncateByPrintWidth(item.productName || '未知商品', 36)
                //         const num = item.num || 0
                //         productInfo += padLeftAlign(prodName, 36) + padRightAlign(num, 12) + '\n'
                //     })
                //     productInfo += createDivider(lineWidth, 'thin') + '\n'
                // }
                // socket.write(iconv.encode(productInfo, 'gbk'))

                // ======================== 底部区域（统一结算小票风格） ========================
                let bottomInfo = ''
                bottomInfo += padLeftAlign('打印时间：', 12) + padRightAlign(data.printTime || new Date().toLocaleString(), 36) + '\n'
                bottomInfo += createDivider(lineWidth, 'full') + '\n'
                // 增加空行，避免打印内容紧贴纸边
                bottomInfo += '\n\n'
                bottomInfo += '\n\n'

                socket.write(iconv.encode(bottomInfo, 'gbk'))
                // 切纸指令（统一结算小票的切纸逻辑）
                socket.write(Buffer.from([0x1D, 0x56, 0x00]))

                // 统一延迟关闭连接，保证数据完整发送
                setTimeout(() => {
                    socket.end()
                    resolve('success')
                }, 400)
            } catch (err) {
                console.error('打印交班小票异常：', err)
                socket.destroy()
                resolve('error')
            }
        })

        // 统一错误处理（对齐结算小票的错误处理逻辑）
        socket.on('error', (err) => {
            console.error('打印机连接错误：', err)
            resolve('error')
        })
        socket.on('timeout', () => resolve('timeout'))
        socket.on('close', () => { })
    })
}