'use strict';

const fs = require('fs');
const path = require('path');
const { execFileSync } = require('child_process');
const logger = require('../../utils/logger');
const evidence = require('./billing-print.evidence');
const {
  BASE_PRN_NAME,
  DEFAULT_SPOOL_DIR,
  DEFAULT_CAPTURE_PORT,
  getBillingPrintConfig,
  saveBillingPrintConfig,
  expectedCaptureFile,
} = require('./billing-print.config');

const CAPTURE_PRINTER_NAME = 'ARKHON Ticket Capture';
const CAPTURE_PORT_NAME = DEFAULT_CAPTURE_PORT;
const CAPTURE_DRIVER_NAME = 'Generic / Text Only';

let windowsAdapter = null;
const state = {
  captureLastCheckedAt: null,
  captureLastRepairAt: null,
  captureLastError: null,
  lastTicketCapturedAt: null,
  lastSoftRestaurantJobAt: null,
  lastEventSignatures: new Map(),
};

function nowIso() {
  return new Date().toISOString();
}

function escapePs(value) {
  return String(value || '').replace(/'/g, "''");
}

function emitOperationalEvent(level, event, payload = {}, signature = '') {
  const key = `${event}:${signature}`;
  if (state.lastEventSignatures.get(event) === key) return;
  state.lastEventSignatures.set(event, key);
  const record = { event, ...payload };
  const fn = level === 'warn' ? logger.warn : logger.info;
  fn.call(logger, record);
  try { evidence.appendLog('ticket-capture.log', event, payload); } catch (_) {}
}

function runPsJson(script, fallback = null) {
  if (process.platform !== 'win32') return fallback;
  const out = execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', script], {
    encoding: 'utf8',
    timeout: 20000,
    windowsHide: true,
  }).trim();
  return out ? JSON.parse(out) : fallback;
}

function runPs(script) {
  if (process.platform !== 'win32') return { ok: false, status: 'unsupported_os', error: 'Ticket Capture solo se instala en Windows.' };
  try {
    execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', script], {
      stdio: 'pipe',
      timeout: 30000,
      windowsHide: true,
    });
    return { ok: true };
  } catch (err) {
    return { ok: false, status: 'error', error: err.stderr ? err.stderr.toString().trim() : err.message };
  }
}

function defaultWindowsAdapter() {
  return {
    inspect({ printerName, portName }) {
      if (process.platform !== 'win32') {
        return { supported: false, spoolerRunning: false, printer: null, port: null };
      }
      const ps = [
        `$ErrorActionPreference = 'Stop'`,
        `$printerName = '${escapePs(printerName)}'`,
        `$portName = '${escapePs(portName)}'`,
        `$spooler = Get-Service -Name Spooler -ErrorAction SilentlyContinue`,
        `$printer = Get-Printer -Name $printerName -ErrorAction SilentlyContinue`,
        `$port = Get-PrinterPort -Name $portName -ErrorAction SilentlyContinue`,
        `$printLog = Get-WinEvent -ListLog 'Microsoft-Windows-PrintService/Operational' -ErrorAction SilentlyContinue`,
        `$jobs = @()`,
        `try { $jobs = @(Get-Printer | ForEach-Object { $pn=$_.Name; Get-PrintJob -PrinterName $pn -ErrorAction SilentlyContinue | ForEach-Object { [pscustomobject]@{ PrinterName=$pn; DocumentName=$_.DocumentName; SubmittedTime=$_.SubmittedTime; JobStatus=[string]$_.JobStatus } } }) } catch { $jobs = @() }`,
        `$events = @()`,
        `try { $events = @(Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-PrintService/Operational'; Id=307; StartTime=(Get-Date).AddDays(-7)} -MaxEvents 50 -ErrorAction SilentlyContinue | ForEach-Object { [pscustomobject]@{ TimeCreated=$_.TimeCreated; DocumentName=[string]$_.Properties[0].Value; PrinterName=[string]$_.Properties[3].Value; PortName=[string]$_.Properties[4].Value } }) } catch { $events = @() }`,
        `[pscustomobject]@{`,
        `  supported = $true;`,
        `  spoolerRunning = [bool]($spooler -and $spooler.Status -eq 'Running');`,
        `  printer = if ($printer) { [pscustomobject]@{ Name=$printer.Name; DriverName=$printer.DriverName; PortName=$printer.PortName; PrinterStatus=[string]$printer.PrinterStatus; WorkOffline=[bool]$printer.WorkOffline } } else { $null };`,
        `  port = if ($port) { [pscustomobject]@{ Name=$port.Name } } else { $null };`,
        `  jobs = $jobs;`,
        `  printEvents = $events`,
        `  printEventLogEnabled = [bool]($printLog -and $printLog.IsEnabled)`,
        `} | ConvertTo-Json -Depth 5`,
      ].join('\n');
      return runPsJson(ps, { supported: true, spoolerRunning: false, printer: null, port: null });
    },
    repair({ printerName, portName, driverName }) {
      const ps = [
        `$ErrorActionPreference = 'Stop'`,
        `$printerName = '${escapePs(printerName)}'`,
        `$portName = '${escapePs(portName)}'`,
        `$driverName = '${escapePs(driverName)}'`,
        `$spooler = Get-Service -Name Spooler -ErrorAction SilentlyContinue`,
        `if (-not $spooler -or $spooler.Status -ne 'Running') { throw 'spooler_stopped: Windows Print Spooler no esta Running.' }`,
        `try { & wevtutil.exe sl 'Microsoft-Windows-PrintService/Operational' /e:true | Out-Null } catch { }`,
        `if (-not (Get-PrinterDriver -Name $driverName -ErrorAction SilentlyContinue)) { Add-PrinterDriver -Name $driverName -ErrorAction Stop }`,
        `if (-not (Get-PrinterDriver -Name $driverName -ErrorAction SilentlyContinue)) { throw ('capture_driver_missing: ' + $driverName) }`,
        `if (-not (Get-PrinterPort -Name $portName -ErrorAction SilentlyContinue)) { Add-PrinterPort -Name $portName }`,
        `$printer = Get-Printer -Name $printerName -ErrorAction SilentlyContinue`,
        `if (-not $printer) { Add-Printer -Name $printerName -DriverName $driverName -PortName $portName }`,
        `else { Set-Printer -Name $printerName -DriverName $driverName -PortName $portName }`,
      ].join('\n');
      return runPs(ps);
    },
    listPrinters() {
      if (process.platform !== 'win32') return { ok: false, supported: false, printers: [] };
      const ps = [
        `$ErrorActionPreference = 'Stop'`,
        `$default = (Get-CimInstance Win32_Printer -Filter "Default=True" -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Name)`,
        `Get-Printer | Sort-Object Name | ForEach-Object {`,
        `  [pscustomobject]@{ Name=$_.Name; DriverName=$_.DriverName; PortName=$_.PortName; Shared=[bool]$_.Shared; Default=($_.Name -eq $default); WorkOffline=[bool]$_.WorkOffline }`,
        `} | ConvertTo-Json -Depth 4`,
      ].join('\n');
      return { ok: true, supported: true, printers: asArray(runPsJson(ps, [])) };
    },
  };
}

function adapter() {
  return windowsAdapter || defaultWindowsAdapter();
}

function setWindowsAdapter(mock) {
  windowsAdapter = mock;
}

function clearWindowsAdapter() {
  windowsAdapter = null;
}

function classifyError(error) {
  const text = String(error || '');
  if (/unsupported_os|solo se instala en Windows/i.test(text)) return 'unsupported_os';
  if (/spooler_stopped|Print Spooler/i.test(text)) return 'spooler_stopped';
  if (/capture_driver_missing/i.test(text)) return 'error';
  return 'error';
}

function asArray(value) {
  if (!value) return [];
  return Array.isArray(value) ? value : [value];
}

function recentPrnFiles(watchFolder, limit = 10) {
  try {
    if (!watchFolder || !fs.statSync(watchFolder).isDirectory()) return [];
    return fs.readdirSync(watchFolder, { withFileTypes: true })
      .filter((entry) => entry.isFile() && path.extname(entry.name).toLowerCase() === '.prn')
      .map((entry) => {
        const fullPath = path.join(watchFolder, entry.name);
        const stat = fs.statSync(fullPath);
        return { name: entry.name, path: fullPath, size: stat.size, modifiedAt: stat.mtime.toISOString() };
      })
      .sort((a, b) => Date.parse(b.modifiedAt) - Date.parse(a.modifiedAt))
      .slice(0, limit);
  } catch (_) {
    return [];
  }
}

function isSoftRestaurantDocument(value) {
  return /soft\s*restaurant|foxpro|trabajo de foxpro|nota de consumo/i.test(String(value || ''));
}

function softRestaurantJobDiagnostic(inspect, printerName, prnFiles = []) {
  const jobs = asArray(inspect?.jobs).map((job) => ({
    printerName: String(job.PrinterName || job.printerName || ''),
    documentName: String(job.DocumentName || job.documentName || ''),
    submittedAt: job.SubmittedTime || job.submittedAt || null,
    status: String(job.JobStatus || job.status || ''),
    source: 'active_job',
  }));
  const events = asArray(inspect?.printEvents).map((job) => ({
    printerName: String(job.PrinterName || job.printerName || ''),
    documentName: String(job.DocumentName || job.documentName || ''),
    submittedAt: job.TimeCreated || job.submittedAt || null,
    portName: String(job.PortName || job.portName || ''),
    source: 'print_event_307',
  }));
  const capturedEvidence = [];
  if (state.lastSoftRestaurantJobAt) capturedEvidence.push({
    printerName,
    documentName: BASE_PRN_NAME,
    submittedAt: state.lastSoftRestaurantJobAt,
    source: 'watcher_prn',
  });
  if (prnFiles[0]) capturedEvidence.push({
    printerName,
    documentName: prnFiles[0].name,
    submittedAt: prnFiles[0].modifiedAt,
    source: 'spool_file',
  });
  const relevant = [...jobs, ...events, ...capturedEvidence]
    .filter((job) => isSoftRestaurantDocument(job.documentName))
    .sort((a, b) => Date.parse(b.submittedAt || 0) - Date.parse(a.submittedAt || 0));
  const latest = relevant[0] || null;
  if (!latest) {
    return {
      softRestaurantPrintingToCapture: null,
      softRestaurantLastJob: null,
      softRestaurantOperationalError: null,
    };
  }
  const matches = latest.printerName.localeCompare(printerName, undefined, { sensitivity: 'accent' }) === 0;
  return {
    softRestaurantPrintingToCapture: matches,
    softRestaurantLastJob: latest,
    softRestaurantOperationalError: matches ? null : 'SoftRestaurant no está apuntando a ARKHON Ticket Capture',
  };
}

function shouldMigrateLegacyFolder(folder) {
  const resolved = path.resolve(String(folder || ''));
  const legacyProgramData = path.resolve(process.env.ProgramData || 'C:\\ProgramData', 'ARKHON Monitor', 'billing-prn');
  const userProfile = process.env.USERPROFILE ? path.resolve(process.env.USERPROFILE).toLowerCase() : '';
  const lower = resolved.toLowerCase();
  const userDocuments = Boolean(userProfile && lower.startsWith(`${userProfile}${path.sep}`) && /[\\/](documents|documentos)([\\/]|$)/i.test(resolved));
  return resolved.toLowerCase() === legacyProgramData.toLowerCase()
    || userDocuments;
}

function ensureSpoolFolder(folder) {
  fs.mkdirSync(folder, { recursive: true });
  if (process.platform !== 'win32' || windowsAdapter) return;
  try {
    execFileSync('icacls.exe', [
      folder,
      '/inheritance:e',
      '/grant',
      '*S-1-5-18:(OI)(CI)F',
      '*S-1-5-32-544:(OI)(CI)F',
      '*S-1-5-32-545:(OI)(CI)M',
    ], { stdio: 'pipe', timeout: 10000, windowsHide: true });
  } catch (error) {
    logger.warn({ event: 'billing_print_spool_acl_failed', folder, error: error.message });
  }
}

function provisionTicketCapture(config = getBillingPrintConfig()) {
  const watchFolder = shouldMigrateLegacyFolder(config.watchFolder) ? DEFAULT_SPOOL_DIR : (config.watchFolder || DEFAULT_SPOOL_DIR);
  const basePrnName = path.basename(config.basePrnName || BASE_PRN_NAME);
  const desired = expectedCaptureFile({ watchFolder, basePrnName });
  ensureSpoolFolder(watchFolder);
  return saveBillingPrintConfig({
    watchFolder,
    basePrnName,
    capturePrinterName: CAPTURE_PRINTER_NAME,
    capturePortName: desired,
    captureDriverName: CAPTURE_DRIVER_NAME,
    captureEnabled: true,
    captureAutoRepair: true,
  });
}

function diagnosticFromInspect(inspect, config = getBillingPrintConfig()) {
  const printerName = CAPTURE_PRINTER_NAME;
  const portName = expectedCaptureFile(config);
  const driverName = CAPTURE_DRIVER_NAME;
  const supported = inspect?.supported !== false && process.platform === 'win32' || Boolean(windowsAdapter);
  const printer = inspect?.printer || null;
  const port = inspect?.port || null;
  const spoolerRunning = inspect?.spoolerRunning === true;
  const capturePrinterInstalled = Boolean(printer);
  const portReady = Boolean(port);
  const actualPortName = String(printer?.PortName || '');
  const actualDriverName = String(printer?.DriverName || '');
  const portMatches = capturePrinterInstalled && actualPortName.toLowerCase() === portName.toLowerCase();
  const driverMatches = capturePrinterInstalled && actualDriverName.toLowerCase() === driverName.toLowerCase();
  const printerReady = capturePrinterInstalled && portMatches && driverMatches && spoolerRunning && portReady;
  const prnFiles = recentPrnFiles(config.watchFolder);
  const expectedFileExists = fs.existsSync(portName);
  const softRestaurant = softRestaurantJobDiagnostic(inspect, printerName, prnFiles);
  let captureStatus = 'missing';
  let captureLastError = state.captureLastError;
  if (!supported) captureStatus = 'unsupported_os';
  else if (!spoolerRunning) captureStatus = 'spooler_stopped';
  else if (printerReady) {
    captureStatus = 'installed';
    captureLastError = null;
  } else if (!capturePrinterInstalled) captureStatus = 'missing';
  else if (!portMatches || !portReady) captureStatus = 'port_mismatch';
  else if (!driverMatches) captureStatus = 'driver_mismatch';
  else captureStatus = 'error';

  if (captureStatus === 'installed') {
    emitOperationalEvent('info', 'CAPTURE_PRINTER_READY', { printerName, driverName, portName }, `${driverName}|${portName}`);
  } else if (captureStatus === 'port_mismatch') {
    emitOperationalEvent('warn', 'CAPTURE_PORT_MISMATCH', { printerName, actualPortName, expectedPortName: portName, portInstalled: portReady }, `${actualPortName}|${portName}|${portReady}`);
  } else if (captureStatus === 'driver_mismatch') {
    emitOperationalEvent('warn', 'CAPTURE_DRIVER_MISMATCH', { printerName, actualDriverName, expectedDriverName: driverName }, `${actualDriverName}|${driverName}`);
  }
  if (!expectedFileExists) {
    emitOperationalEvent('warn', 'CAPTURE_FILE_NOT_FOUND', { expectedFile: portName }, portName);
  }
  if (softRestaurant.softRestaurantPrintingToCapture === false) {
    emitOperationalEvent('warn', 'SOFTRESTAURANT_NOT_PRINTING_TO_CAPTURE', {
      error: softRestaurant.softRestaurantOperationalError,
      latestJob: softRestaurant.softRestaurantLastJob,
    }, `${softRestaurant.softRestaurantLastJob?.printerName}|${softRestaurant.softRestaurantLastJob?.submittedAt}`);
  }

  return {
    capture_status: captureStatus,
    capturePrinterInstalled,
    capturePrinterReady: captureStatus === 'installed',
    capturePrinterName: printerName,
    capturePortName: portName,
    capturePrinterPortName: actualPortName || null,
    captureExpectedFile: portName,
    captureExpectedFileExists: expectedFileExists,
    captureConfiguredPortName: config.capturePortName || null,
    capturePortMatches: portMatches,
    captureDriverName: actualDriverName || driverName,
    captureExpectedDriverName: driverName,
    captureDriverMatches: driverMatches,
    capturePortInstalled: portReady,
    recentPrnFiles: prnFiles,
    captureActiveJobs: asArray(inspect?.jobs).filter((job) => String(job.PrinterName || job.printerName || '').toLowerCase() === printerName.toLowerCase()),
    capturePrintEventLogEnabled: inspect?.printEventLogEnabled === true,
    ...softRestaurant,
    spoolerRunning,
    captureRepairAvailable: supported === true,
    captureLastCheckedAt: state.captureLastCheckedAt || config.captureLastCheckedAt || null,
    captureLastRepairAt: state.captureLastRepairAt || config.captureLastRepairAt || null,
    captureLastError,
    lastTicketCapturedAt: state.lastTicketCapturedAt,
    lastSoftRestaurantJobAt: state.lastSoftRestaurantJobAt,
  };
}

function inspectTicketCapture(config = getBillingPrintConfig()) {
  state.captureLastCheckedAt = nowIso();
  const portName = expectedCaptureFile(config);
  try {
    return diagnosticFromInspect(adapter().inspect({
      printerName: CAPTURE_PRINTER_NAME,
      portName,
    }), config);
  } catch (err) {
    state.captureLastError = err.message;
    return {
      ...diagnosticFromInspect(null, config),
      capture_status: classifyError(err.message),
      capturePrinterReady: false,
      captureLastError: err.message,
    };
  }
}

function repairTicketCapture(config = getBillingPrintConfig()) {
  config = provisionTicketCapture(config);
  state.captureLastCheckedAt = nowIso();
  saveBillingPrintConfig({ captureLastCheckedAt: state.captureLastCheckedAt });
  const portName = expectedCaptureFile(config);
  let before = null;
  try {
    before = adapter().inspect({
      printerName: CAPTURE_PRINTER_NAME,
      portName,
    });
  } catch (_) {}

  const beforeDiagnostic = diagnosticFromInspect(before, config);
  if (beforeDiagnostic.capturePrinterReady) {
    state.captureLastError = null;
    return { ok: true, repaired: false, ...beforeDiagnostic };
  }

  const repaired = adapter().repair({
    printerName: CAPTURE_PRINTER_NAME,
    portName,
    driverName: CAPTURE_DRIVER_NAME,
  });
  if (!repaired.ok) {
    state.captureLastError = repaired.error || repaired.status || 'capture_repair_failed';
    emitOperationalEvent('warn', 'CAPTURE_SETUP_FAILED', { error: state.captureLastError, status: repaired.status || classifyError(repaired.error) }, state.captureLastError);
    return {
      ok: false,
      repaired: false,
      ...beforeDiagnostic,
      capture_status: repaired.status || classifyError(repaired.error),
      capturePrinterReady: false,
      captureLastError: state.captureLastError,
    };
  }

  state.captureLastRepairAt = nowIso();
  state.captureLastError = null;
  saveBillingPrintConfig({ captureLastRepairAt: state.captureLastRepairAt, captureLastCheckedAt: state.captureLastCheckedAt });
  const after = diagnosticFromInspect(adapter().inspect({
    printerName: CAPTURE_PRINTER_NAME,
    portName,
  }), config);
  if (after.capturePrinterReady) {
    if (!beforeDiagnostic.capturePrinterInstalled) emitOperationalEvent('info', 'CAPTURE_PRINTER_CREATED', { printerName: CAPTURE_PRINTER_NAME, portName, driverName: CAPTURE_DRIVER_NAME }, portName);
    else emitOperationalEvent('info', 'CAPTURE_PRINTER_REPAIRED', { printerName: CAPTURE_PRINTER_NAME, portName, driverName: CAPTURE_DRIVER_NAME }, portName);
    if (!beforeDiagnostic.capturePortMatches || !beforeDiagnostic.capturePortInstalled) emitOperationalEvent('info', 'CAPTURE_PORT_REPAIRED', { printerName: CAPTURE_PRINTER_NAME, portName }, portName);
    if (!beforeDiagnostic.captureDriverMatches) emitOperationalEvent('info', 'CAPTURE_DRIVER_REPAIRED', { printerName: CAPTURE_PRINTER_NAME, driverName: CAPTURE_DRIVER_NAME }, CAPTURE_DRIVER_NAME);
  } else {
    emitOperationalEvent('warn', 'CAPTURE_SETUP_FAILED', { status: after.capture_status, error: after.captureLastError }, after.capture_status);
  }
  return { ok: after.capturePrinterReady, repaired: true, ...after, capture_status: after.capturePrinterReady ? 'repaired' : after.capture_status };
}

function isCapturePrinterName(name) {
  return String(name || '').trim().toLowerCase() === CAPTURE_PRINTER_NAME.toLowerCase();
}

function physicalPrinterCandidate(printer) {
  const name = String(printer.Name || printer.name || '').trim();
  const driver = String(printer.DriverName || printer.driverName || '').trim();
  const port = String(printer.PortName || printer.portName || '').trim();
  const virtualPrinter = /(?:fax|microsoft\s+print\s+to\s+pdf|xps|onenote|pdfcreator|adobe\s+pdf)/i.test(`${name} ${driver}`)
    || /^(?:PORTPROMPT:|SHRFAX:|FILE:|NUL:)$/i.test(port);
  return {
    name,
    driverName: driver,
    portName: port,
    shared: printer.Shared === true || printer.shared === true,
    default: printer.Default === true || printer.default === true,
    workOffline: printer.WorkOffline === true || printer.workOffline === true,
    isCapturePrinter: isCapturePrinterName(name),
    // Generic / Text Only tambien es un controlador valido para muchas
    // termicas. Se excluye solamente nuestra impresora de captura, no el driver.
    isPhysicalCandidate: Boolean(name) && !isCapturePrinterName(name) && !virtualPrinter,
  };
}

function listOutputPrinters() {
  try {
    const result = adapter().listPrinters ? adapter().listPrinters() : { ok: true, supported: Boolean(windowsAdapter), printers: [] };
    const printers = asArray(result?.printers).map(physicalPrinterCandidate);
    return {
      ok: result?.ok !== false,
      supported: result?.supported !== false,
      capturePrinterName: CAPTURE_PRINTER_NAME,
      printers,
      physicalPrinters: printers.filter((printer) => printer.isPhysicalCandidate),
    };
  } catch (err) {
    return { ok: false, supported: process.platform === 'win32' || Boolean(windowsAdapter), error: err.message, capturePrinterName: CAPTURE_PRINTER_NAME, printers: [], physicalPrinters: [] };
  }
}

function saveOutputPrinterName(outputPrinterName) {
  const name = String(outputPrinterName || '').trim();
  if (!name) return { ok: false, error: 'outputPrinterName requerido' };
  if (isCapturePrinterName(name)) return { ok: false, error: 'outputPrinterName no puede ser ARKHON Ticket Capture' };
  const listed = listOutputPrinters();
  const known = listed.physicalPrinters.find((printer) => printer.name.toLowerCase() === name.toLowerCase());
  const widthMatch = `${known?.name || name} ${known?.driverName || ''}`.match(/(?:^|\D)(58|80)(?:\D|$)/);
  const inferredWidth = widthMatch ? Number(widthMatch[1]) : null;
  const config = saveBillingPrintConfig({
    outputPrinterName: name,
    printerName: name,
    enabled: true,
    autoStart: true,
    ...(inferredWidth ? { paperWidthMm: inferredWidth, qrPaperWidth: inferredWidth } : {}),
  });
  return {
    ok: true,
    outputPrinterName: config.outputPrinterName,
    outputPrinterAvailable: known ? true : null,
    warning: listed.supported && listed.printers.length && !known ? 'outputPrinterName no aparece en la lista actual de impresoras fisicas.' : null,
    printers: listed.physicalPrinters,
    config,
  };
}

function ensureTicketCapture(config = getBillingPrintConfig()) {
  if (config.captureEnabled === false) {
    return {
      ok: true,
      capture_status: 'disabled_by_license',
      capturePrinterInstalled: false,
      capturePrinterReady: false,
      capturePrinterName: CAPTURE_PRINTER_NAME,
      capturePortName: expectedCaptureFile(config),
      captureExpectedFile: expectedCaptureFile(config),
      captureDriverName: CAPTURE_DRIVER_NAME,
      spoolerRunning: false,
      captureRepairAvailable: false,
      captureLastCheckedAt: state.captureLastCheckedAt || config.captureLastCheckedAt || null,
      captureLastRepairAt: state.captureLastRepairAt || config.captureLastRepairAt || null,
      captureLastError: null,
      lastTicketCapturedAt: state.lastTicketCapturedAt,
    };
  }
  const current = inspectTicketCapture(config);
  if (current.capturePrinterReady || config.captureAutoRepair === false) return { ok: current.capturePrinterReady, ...current };
  return repairTicketCapture(config);
}

async function testPrintTicketCapture(config = getBillingPrintConfig()) {
  const diagnostic = inspectTicketCapture(config);
  if (!diagnostic.capturePrinterReady) return { ok: false, accepted: false, ...diagnostic };
  const watcher = require('./billing-print.watcher');
  const result = await watcher.createAndProcessCaptureTestPrn(config);
  return {
    ok: result?.ok === true,
    accepted: Boolean(result?.testFile),
    watcherProcessed: result?.ok === true,
    testFile: result?.testFile || null,
    processing: result,
    ...inspectTicketCapture(config),
  };
}

function markTicketCaptured(at = nowIso(), options = {}) {
  state.lastTicketCapturedAt = at;
  if (options.source === 'softrestaurant_real') state.lastSoftRestaurantJobAt = at;
}

module.exports = {
  CAPTURE_PRINTER_NAME,
  CAPTURE_PORT_NAME,
  CAPTURE_DRIVER_NAME,
  inspectTicketCapture,
  repairTicketCapture,
  ensureTicketCapture,
  provisionTicketCapture,
  testPrintTicketCapture,
  listOutputPrinters,
  saveOutputPrinterName,
  markTicketCaptured,
  setWindowsAdapter,
  clearWindowsAdapter,
};
