const fs = require("fs");
const { execSync } = require("child_process");

function shellArg(value) {
  return `'${String(value).replace(/'/g, `'\\''`)}'`;
}

function listCupsPrinterNames() {
  try {
    const out = execSync("lpstat -p 2>/dev/null", {
      encoding: "utf8",
      timeout: 5000,
    });
    const names = [];
    for (const line of out.split("\n")) {
      const m = line.match(/printer\s+(\S+)/i);
      if (m) names.push(m[1]);
    }
    return names;
  } catch {
    return [];
  }
}

/** Best CUPS queue for ESC/POS raw printing on Linux. */
function resolveLinuxDevicePath(hints = {}) {
  const raw = String(hints.devicePath || "");
  const fromUri = raw.match(/(\/dev\/(?:usb\/)?lp\d+)/);
  if (fromUri) return fromUri[1];
  try {
    const out = execSync(
      'ls /dev/usb/lp* /dev/lp* 2>/dev/null | head -1',
      { encoding: "utf8", timeout: 3000 },
    );
    const path = out.trim();
    return path && fs.existsSync(path) ? path : null;
  } catch {
    return null;
  }
}

function resolveLinuxCupsQueueName(hints = {}) {
  const env = (process.env.PRINTER_SPOOLER_NAME || "").trim();
  if (env) return env;
  if (hints.cupsQueueName) return hints.cupsQueueName;
  if (
    hints.name &&
    (hints.type === "CUPS" || hints.type === "Network/CUPS")
  ) {
    return hints.name;
  }

  const names = listCupsPrinterNames();
  const prefer = (re) => names.find((n) => re.test(n));
  return prefer(/epson|tm-|thermal/i) || names[0] || null;
}

function getCupsQueueStatus(queueName) {
  if (!queueName) return null;
  try {
    let pendingJobs = 0;
    try {
      const pending = execSync(`lpstat -o ${shellArg(queueName)} 2>/dev/null`, {
        encoding: "utf8",
        timeout: 5000,
      });
      pendingJobs = pending.trim()
        ? pending.trim().split("\n").filter(Boolean).length
        : 0;
    } catch {
      pendingJobs = 0;
    }

    let printerState = "unknown";
    let waiting = false;
    try {
      const pOut = execSync(`lpstat -p ${shellArg(queueName)} 2>/dev/null`, {
        encoding: "utf8",
        timeout: 5000,
      });
      const lines = pOut.split("\n").map((l) => l.trim()).filter(Boolean);
      if (lines[0]) {
        const m = lines[0].match(/printer\s+\S+\s+(.+)/i);
        printerState = m ? m[1].trim() : lines[0];
      }
      waiting = lines.some((line) => /waiting|unable|offline/i.test(line));
    } catch {
      /* ignore */
    }

    return {
      queue: queueName,
      pendingJobs,
      printerState,
      stuck: pendingJobs > 0 && waiting,
    };
  } catch {
    return { queue: queueName, pendingJobs: 0, printerState: "unknown", stuck: false };
  }
}

function clearCupsQueue(queueName) {
  if (!queueName) return { ok: false, error: "NO_QUEUE" };
  try {
    execSync(`cancel -a ${shellArg(queueName)} 2>/dev/null || true`, {
      timeout: 15000,
      stdio: "ignore",
    });
    return { ok: true, queue: queueName };
  } catch (e) {
    return { ok: true, queue: queueName, note: String(e.message || e) };
  }
}

/** Clear only when CUPS reports a blocked queue (common on register PCs). */
function clearCupsQueueIfStuck(queueName) {
  const status = getCupsQueueStatus(queueName);
  if (status?.stuck) {
    return clearCupsQueue(queueName);
  }
  return { ok: true, skipped: true, queue: queueName };
}

function printRawViaCups(queueName, filePath) {
  if (!queueName) throw new Error("No CUPS queue configured");
  if (!fs.existsSync(filePath)) throw new Error(`Print file not found: ${filePath}`);
  const out = execSync(
    `lp -d ${shellArg(queueName)} -o raw ${shellArg(filePath)} 2>&1`,
    { encoding: "utf8", timeout: 15000 },
  );
  const m = out.match(/request id is (\S+)/i);
  return { jobId: m ? m[1] : null, output: out.trim() };
}

function writeRawToDevice(devicePath, filePath) {
  if (!devicePath) throw new Error("No device path");
  if (!fs.existsSync(filePath)) throw new Error(`Print file not found: ${filePath}`);
  const data = fs.readFileSync(filePath);
  fs.writeFileSync(devicePath, data);
}

module.exports = {
  listCupsPrinterNames,
  resolveLinuxCupsQueueName,
  resolveLinuxDevicePath,
  getCupsQueueStatus,
  clearCupsQueue,
  clearCupsQueueIfStuck,
  printRawViaCups,
  writeRawToDevice,
};
