"use strict";
// `hub-agent test ...` — exercise an endpoint locally by piping a synthetic
// CommandMessage through the same dispatcher the WS receive path uses.
// With multiple printers / drawers / scanners, endpoint name pinpoints
// which one — no parallel test code to drift out of sync with the real
// handler.
//
//   hub-agent test <endpoint> [operation] [json-data]
//
// <endpoint> can be either:
//   • a name from config.json (e.g. "stmicroelectronics-pos80-printer")
//   • a raw URI to test an unconfigured CUPS queue or network printer
//       printer:NAME              → CUPS queue, ESC/POS-via-lp-raw
//       tcp://host:port           → network ESC/POS printer
//       text:NAME                 → CUPS queue, plain text (laser/office)
//
//   `text:` lets you smoke-test laser printers that can't render ESC/POS.
//
// Operation defaults: printer→print, drawer→drawer, scanner→listen.
//
// Examples:
//   hub-agent test                                              # list endpoints
//   hub-agent test stmicroelectronics-pos80-printer             # print receipt
//   hub-agent test stmicroelectronics-pos80-printer drawer      # kick drawer
//   hub-agent test printer:HP_Color_LaserJet_MFP_M281fdw__DF762B_   # ad-hoc CUPS
//   hub-agent test text:HP_Color_LaserJet_MFP_M281fdw__DF762B_      # plain text
//   hub-agent test cu-usbserial-110 listen                      # scanner echo
Object.defineProperty(exports, "__esModule", { value: true });
exports.testEndpoint = testEndpoint;
const dispatcher_1 = require("./dispatcher");
const scanner_1 = require("./handlers/scanner");
function loadCfg() {
    try {
        const { loadConfig } = require('./config');
        return loadConfig();
    }
    catch {
        return null;
    }
}
function sampleData(operation, endpointName) {
    switch (operation) {
        case 'print':
            return {
                ticketType: 'receipt',
                lines: [
                    { kind: 'text', text: '*** HUB AGENT TEST ***', bold: true, size: 'large' },
                    { kind: 'text', text: '' },
                    { kind: 'text', text: `endpoint: ${endpointName}` },
                    { kind: 'text', text: `time: ${new Date().toLocaleString()}` },
                    { kind: 'feed', lines: 3 },
                    { kind: 'cut' },
                ],
            };
        case 'drawer':
            return { reason: 'hub-agent test drawer' };
        case 'page':
            return { phone: '+10000000000', message: 'hub-agent test page' };
        case 'display':
            return { lines: ['hub-agent', 'test display'] };
        default:
            return {};
    }
}
// Default operation per endpoint kind. Printer endpoints get `print` —
// drawer test is a separate explicit choice (`hub-agent test PRINTER drawer`).
function defaultOperation(kind) {
    switch (kind) {
        case 'printer': return 'print';
        case 'drawer': return 'drawer';
        case 'scanner': return 'listen';
        case 'pager': return 'page';
        case 'display': return 'display';
        case 'kds': return 'kds';
        default: return null;
    }
}
async function testEndpoint(endpointName, operation, dataJson) {
    const cfg = loadCfg();
    if (!cfg) {
        console.error('No config — run "hub-agent setup" first.');
        process.exit(1);
    }
    const endpoints = cfg.endpoints || [];
    if (!endpointName) {
        console.error('Usage: hub-agent test <endpoint> [operation] [json-data]');
        console.error('');
        console.error('Configured endpoints:');
        for (const e of endpoints) {
            console.error(`  ${e.name.padEnd(40)} [${e.kind}]  capabilities: ${(e.capabilities || []).join(', ') || '-'}`);
        }
        console.error('');
        console.error('Pick one and pass its name as the first arg.');
        process.exit(1);
    }
    // Synthetic endpoints — let the operator test ANY CUPS queue or network
    // printer without first adding it to config. Three URI shapes recognised:
    //
    //   printer:NAME      → CUPS queue, send ESC/POS bytes (thermal POS)
    //   tcp://host:port   → network printer, send ESC/POS bytes
    //   text:NAME         → CUPS queue, send plain text via `lp` (no -o raw)
    //                       Use for laser / office printers that can't
    //                       interpret ESC/POS.
    let ep;
    if (/^printer:|^tcp:\/\//i.test(endpointName)) {
        ep = {
            name: `adhoc-${Date.now()}`,
            kind: 'printer',
            capabilities: ['print', 'drawer'],
            config: { interface: endpointName, driver: 'epson' },
        };
        console.log(`hub-agent test → ad-hoc endpoint interface=${endpointName}`);
    }
    else if (/^text:/i.test(endpointName)) {
        const queue = endpointName.slice('text:'.length);
        return testTextPrint(queue);
    }
    else {
        ep = endpoints.find((e) => e.name === endpointName);
        if (!ep) {
            console.error(`No endpoint named "${endpointName}".`);
            console.error('Configured: ' + endpoints.map((e) => e.name).join(', '));
            console.error('');
            console.error('To test an OS-detected printer that isn\'t in config, prefix with');
            console.error('  printer:<cups-name>   (thermal / ESC-POS)');
            console.error('  text:<cups-name>      (laser / office, plain text)');
            console.error('  tcp://host:port       (network ESC-POS)');
            process.exit(1);
        }
    }
    const op = operation || defaultOperation(ep.kind) || (ep.capabilities && ep.capabilities[0]);
    if (!op) {
        console.error(`Endpoint "${endpointName}" has no default operation. Pass one explicitly.`);
        process.exit(1);
    }
    // Scanner is event-source, not command-target. Treat `listen` as a
    // special CLI verb that opens the device for 30s and prints scans.
    // Note: while the service is running it OWNS /dev/cu.* — you must stop
    // the service first OR run this from a different host.
    if (op === 'listen' || ep.kind === 'scanner') {
        return testScannerListen(ep);
    }
    let data;
    if (dataJson) {
        try {
            data = JSON.parse(dataJson);
        }
        catch (e) {
            console.error(`--data is not valid JSON: ${e.message}`);
            process.exit(1);
        }
    }
    else {
        data = sampleData(op, endpointName);
    }
    console.log(`hub-agent test → endpoint=${endpointName} operation=${op}`);
    // Build a CommandMessage exactly as the WS receive path would. Routing
    // it through dispatchCommand means this test takes the SAME code path as
    // a real command from the server — bugs surface in both places at once.
    const cmd = {
        type: 'command',
        commandId: `cli-${Date.now()}`,
        capability: op,
        endpointName: ep.name,
        data,
    };
    const response = await (0, dispatcher_1.dispatchCommand)({ endpoints }, cmd);
    console.log('result:', JSON.stringify(response));
    if (!response.ok)
        process.exit(2);
}
// Plain-text print via `lp -d QUEUE`. No -o raw, so CUPS runs the file
// through the queue's filter chain — that's what laser / inkjet drivers
// expect. Use this to smoke-test that the agent → CUPS → printer pipeline
// works on a queue that can't speak ESC/POS.
async function testTextPrint(queue) {
    const { spawn } = require('child_process');
    const text = [
        '*** HUB AGENT TEST ***',
        '',
        `queue: ${queue}`,
        `time:  ${new Date().toLocaleString()}`,
        '',
        'If this prints, the hub → CUPS → printer',
        'pipeline is healthy for this queue.',
        '',
    ].join('\n') + '\n\n\n';
    console.log(`hub-agent test → text print to CUPS queue "${queue}"`);
    await new Promise((resolve, reject) => {
        const proc = spawn('lp', ['-d', queue], { stdio: ['pipe', 'pipe', 'pipe'] });
        let stdout = '';
        let stderr = '';
        proc.stdout.on('data', (b) => { stdout += b.toString(); });
        proc.stderr.on('data', (b) => { stderr += b.toString(); });
        proc.on('error', (e) => reject(new Error(`lp spawn failed: ${e.message}`)));
        proc.on('close', (code) => {
            if (code === 0) {
                console.log(`result: ok ${stdout.trim()}`);
                resolve();
            }
            else {
                console.error(`lp exited ${code}: ${stderr.trim() || 'no stderr'}`);
                reject(new Error(`lp exit ${code}`));
            }
        });
        proc.stdin.write(text);
        proc.stdin.end();
    });
}
async function testScannerListen(ep) {
    const path = ep.config?.path || ep.path;
    if (!path) {
        console.error(`Scanner endpoint "${ep.name}" has no path in config.`);
        process.exit(1);
    }
    console.log(`hub-agent test → scanner ${ep.name} (${path})`);
    console.log('Note: if the service is running it already owns this device;');
    console.log('stop it first with "sudo launchctl bootout system /Library/LaunchDaemons/io.appmint.hub.plist" (macOS)');
    console.log('or "sudo systemctl stop appmint-hub" (Linux), then re-run.');
    console.log('Scan a barcode — listening for 30s, Ctrl-C to stop early.');
    console.log('');
    const reader = new scanner_1.ScannerReader({ path, endpointName: ep.name, minLength: 1 });
    reader.on('scan', (code) => {
        const t = new Date().toISOString().split('T')[1].split('.')[0];
        console.log(`  [${t}]  ${code}`);
    });
    reader.on('error', (e) => {
        console.error(`  scanner error: ${e.message}`);
    });
    try {
        reader.start();
    }
    catch (e) {
        console.error(`Failed to start scanner: ${e.message}`);
        process.exit(1);
    }
    await new Promise((resolve) => setTimeout(resolve, 30000));
    reader.stop();
    console.log('');
    console.log('Done.');
}
//# sourceMappingURL=test-cmd.js.map