const { execFileSync, execSync } = require("child_process");
const fs = require("fs");
const { getConfigPath } = require("./lib/paths");

function runPowerShell(script) {
  return execFileSync(
    "powershell.exe",
    ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script],
    { encoding: "utf8", maxBuffer: 4 * 1024 * 1024, windowsHide: true },
  );
}

function getUsbModule() {
  try {
    return require("usb");
  } catch (err) {
    console.log("⚠️ USB module unavailable:", err.message);
    return null;
  }
}

const THERMAL_VENDOR_IDS = {
  "04b8": "Epson",
  "0416": "WinSystems",
  "15d9": "Star Micronics",
  "05c6": "Citizen",
  "0924": "Bixolon",
  "0411": "Citizen",
  "0a5f": "Zebra",
  "04f9": "Brother",
  "055f": "CITIZEN",
  "1fc9": "XPrinter",
  "0483": "Generic/STM",
  6868: "Rongta",
  1504: "Prisym",
};

/** Names that are not physical receipt printers. */
const VIRTUAL_QUEUE_RE =
  /pdf|xps|generic|file|document|fax|save|microsoft|one note|cups-pdf|virtual|dummy|null/i;

/** Receipt printer queue names — keep even when driver is Generic/Text Only. */
const THERMAL_QUEUE_NAME_RE =
  /\bBC-?95|BC-95AC|receipt|thermal|pos|escpos|80mm|xprinter|tm-|t88|bixolon|star\b|rongta/i;

function printerKey(p) {
  return `${(p.type || "").toLowerCase()}::${(p.name || "").toLowerCase()}`;
}

class PrinterDetector {
  constructor() {
    this.detectedPrinters = [];
    this.selectedPrinter = null;
    this.configFile = getConfigPath();
  }

  async detectAllPrinters() {
    console.log("🔍 Scanning for thermal printers…");
    this.detectedPrinters = [];

    await this.detectUSBPrinters();
    await this.detectCUPSPrinters();
    await this.detectSerialPrinters();
    await this.detectWindowsSpoolerPrinters();
    await this.detectWindowsPrinters();

    this.dedupePrinters();
    console.log(`✅ Found ${this.detectedPrinters.length} printer(s)`);
    return this.detectedPrinters;
  }

  dedupePrinters() {
    const seen = new Set();
    this.detectedPrinters = this.detectedPrinters.filter((p) => {
      const key = printerKey(p);
      if (seen.has(key)) return false;
      seen.add(key);
      return true;
    });
  }

  async detectUSBPrinters() {
    try {
      const usb = getUsbModule();
      if (!usb) return;
      const devices = usb.getDeviceList();
      const lpDevices = this.listLinuxLpDevices();

      devices.forEach((device) => {
        const vendorId = device.deviceDescriptor.idVendor
          .toString(16)
          .padStart(4, "0");
        const productId = device.deviceDescriptor.idProduct
          .toString(16)
          .padStart(4, "0");

        if (!THERMAL_VENDOR_IDS[vendorId]) return;

        let devicePath = "";
        if (process.platform === "linux") {
          devicePath = lpDevices[0] || "";
        } else if (process.platform === "win32") {
          devicePath = `USB\\VID_${vendorId}&PID_${productId}`;
        }

        const printer = {
          type: "USB",
          name: `${THERMAL_VENDOR_IDS[vendorId]} USB (${vendorId}:${productId})`,
          vendorId,
          productId,
          devicePath: devicePath || `/dev/usb/lp0`,
          autoDetected: true,
          platform: process.platform,
          driver: "ESC/POS",
        };

        if (process.platform === "linux") {
          try {
            const { resolveLinuxCupsQueueName } = require("./lib/linux-cups");
            printer.cupsQueueName = resolveLinuxCupsQueueName(printer);
          } catch {
            /* optional */
          }
        }

        this.detectedPrinters.push(printer);
        console.log(`📦 USB thermal: ${printer.name}`);
      });
    } catch (error) {
      console.log("⚠️ USB detection failed:", error.message);
    }
  }

  listLinuxLpDevices() {
    if (process.platform !== "linux") return [];
    try {
      const output = execSync(
        'find /dev/usb -name "lp*" 2>/dev/null; ls /dev/usb/lp* 2>/dev/null',
        { encoding: "utf8" },
      );
      return output
        .split("\n")
        .map((d) => d.trim())
        .filter((d) => d && fs.existsSync(d));
    } catch {
      return [];
    }
  }

  /** Real CUPS queues only — enabled, non-virtual. */
  async detectCUPSPrinters() {
    if (process.platform !== "linux" && process.platform !== "darwin") return;

    try {
      const statusOut = execSync("lpstat -p 2>/dev/null", { encoding: "utf8" });
      const lines = statusOut.split("\n").filter((l) => l.trim());

      for (const line of lines) {
        const match = line.match(/^printer\s+(\S+)\s+(.+)$/i);
        if (!match) continue;

        const printerName = match[1];
        const state = match[2].toLowerCase();

        if (VIRTUAL_QUEUE_RE.test(printerName)) continue;
        if (state.includes("disabled")) continue;

        let deviceUri = "";
        let isThermal = false;
        try {
          const info = execSync(`lpstat -v ${printerName} 2>/dev/null`, {
            encoding: "utf8",
          });
          const uriMatch = info.match(/device for .+?:\s*(.+)$/im);
          deviceUri = uriMatch ? uriMatch[1].trim() : "";
          if (/usb:|serial:|socket:|ipp:\/\//i.test(deviceUri)) {
            isThermal = true;
          }
        } catch {
          /* ignore */
        }

        try {
          const detail = execSync(`lpstat -l -p ${printerName} 2>/dev/null`, {
            encoding: "utf8",
          });
          if (
            /epson|tm-|thermal|receipt|pos|star|bixolon|xprinter|rongta|esc\/pos/i.test(
              detail,
            )
          ) {
            isThermal = true;
          }
        } catch {
          /* ignore */
        }

        if (!isThermal && !/epson|tm-|thermal|receipt|pos|star|bixolon|xprinter/i.test(printerName)) {
          continue;
        }

        this.detectedPrinters.push({
          type: "CUPS",
          name: printerName,
          devicePath: deviceUri || `cups:${printerName}`,
          cupsQueueName: printerName,
          autoDetected: true,
          platform: process.platform,
          driver: "CUPS",
          status: state.includes("idle")
            ? "idle"
            : state.includes("printing")
              ? "printing"
              : "ready",
        });
        console.log(`🖨️ CUPS queue: ${printerName}`);
      }
    } catch {
      /* CUPS not installed or no printers */
    }
  }

  /** USB serial adapters only — never onboard ttyS* ports. */
  async detectSerialPrinters() {
    if (process.platform !== "linux") return;

    try {
      const serialPorts = execSync(
        'ls /dev/ttyUSB* /dev/ttyACM* 2>/dev/null || true',
        { encoding: "utf8" },
      );
      const ports = serialPorts.split("\n").filter((p) => p.trim());

      ports.forEach((port) => {
        this.detectedPrinters.push({
          type: "Serial",
          name: `Serial adapter (${port})`,
          devicePath: port.trim(),
          autoDetected: true,
          platform: process.platform,
          driver: "Serial",
          baudRate: 9600,
        });
        console.log(`📠 Serial adapter: ${port.trim()}`);
      });
    } catch (error) {
      console.log("⚠️ Serial detection failed:", error.message);
    }
  }

  async detectWindowsSpoolerPrinters() {
    if (process.platform !== "win32") return;

    try {
      const output = runPowerShell(
        "Get-Printer | Select-Object Name, DriverName, PortName | ConvertTo-Json -Compress",
      );
      const parsed = JSON.parse(output || "[]");
      const printers = Array.isArray(parsed) ? parsed : [parsed];
      for (const printer of printers) {
        if (!printer?.Name) continue;
        const thermalName = THERMAL_QUEUE_NAME_RE.test(printer.Name);
        if (VIRTUAL_QUEUE_RE.test(printer.Name)) continue;
        if (
          printer.DriverName &&
          VIRTUAL_QUEUE_RE.test(printer.DriverName) &&
          !thermalName
        ) {
          continue;
        }
        this.detectedPrinters.push({
          type: "Spooler",
          name: printer.Name,
          devicePath: printer.Name,
          spoolerName: printer.Name,
          portName: printer.PortName,
          windowsDriver: printer.DriverName,
          autoDetected: true,
          platform: "win32",
          driver: "Windows-Spooler-RAW",
        });
        console.log(`🪟 Spooler: ${printer.Name}`);
      }
    } catch (error) {
      console.log("⚠️ Windows spooler enumeration failed:", error.message);
    }
  }

  async detectWindowsPrinters() {
    if (process.platform !== "win32") return;

    try {
      const output = runPowerShell(
        "Get-CimInstance Win32_Printer | Select-Object Name, PortName, DriverName | ConvertTo-Json -Compress",
      );

      try {
        const printers = JSON.parse(output);
        const list = Array.isArray(printers) ? printers : [printers];
        list.forEach((printer) => {
          if (!printer?.Name) return;
          if (this.detectedPrinters.some((p) => p.name === printer.Name)) {
            return;
          }
          if (
            printer.DriverName &&
            (printer.DriverName.includes("Epson") ||
              printer.DriverName.includes("TM-") ||
              printer.DriverName.includes("Receipt") ||
              printer.DriverName.includes("POS") ||
              printer.DriverName.includes("Star") ||
              printer.DriverName.includes("Bixolon"))
          ) {
            this.detectedPrinters.push({
              type: "Windows",
              name: printer.Name,
              devicePath: printer.PortName || `WinPrint:${printer.Name}`,
              autoDetected: true,
              platform: "win32",
              driver: "Windows",
              windowsDriver: printer.DriverName,
            });
            console.log(`🪟 Windows printer: ${printer.Name}`);
          }
        });
      } catch {
        console.log("⚠️ Could not parse Windows printer list");
      }
    } catch (error) {
      console.log("⚠️ Windows printer detection failed:", error.message);
    }
  }

  autoSelectPrinter() {
    if (this.detectedPrinters.length === 0) {
      console.log("⚠️ No printers detected.");
      this.selectedPrinter = null;
      this.saveConfig();
      return null;
    }

    const envSpooler = (process.env.PRINTER_SPOOLER_NAME || "").trim();
    if (envSpooler) {
      const match =
        this.detectedPrinters.find((p) => p.name === envSpooler) ||
        this.detectedPrinters.find((p) => p.type === "Spooler");
      this.selectedPrinter = match || {
        type: "Spooler",
        name: envSpooler,
        devicePath: envSpooler,
        spoolerName: envSpooler,
        autoDetected: true,
        platform: process.platform,
        driver: "Windows-Spooler-RAW",
      };
      console.log(`✅ Using PRINTER_SPOOLER_NAME: ${envSpooler}`);
      this.saveConfig();
      return this.selectedPrinter;
    }

    const priority =
      process.platform === "linux"
        ? ["CUPS", "USB", "Spooler", "Serial", "Windows"]
        : ["Spooler", "USB", "Windows", "CUPS", "Serial"];

    for (const type of priority) {
      const printer = this.detectedPrinters.find((p) => p.type === type);
      if (printer) {
        console.log(`✅ Auto-selected: ${printer.name} (${printer.type})`);
        this.selectedPrinter = printer;
        this.saveConfig();
        return printer;
      }
    }

    this.selectedPrinter = this.detectedPrinters[0];
    console.log(`✅ Selected: ${this.selectedPrinter.name}`);
    this.saveConfig();
    return this.selectedPrinter;
  }

  getFallbackPrinter() {
    return {
      type: "USB",
      name: "No printer configured",
      devicePath: process.platform === "win32" ? "USB" : "/dev/usb/lp0",
      autoDetected: false,
      isFallback: true,
      platform: process.platform,
      driver: "ESC/POS",
    };
  }

  saveConfig() {
    const config = {
      selectedPrinter: this.selectedPrinter,
      detectedPrinters: this.detectedPrinters,
      lastDetection: new Date().toISOString(),
      version: "1.1.0",
    };

    fs.writeFileSync(this.configFile, JSON.stringify(config, null, 2));
    console.log(`💾 Saved printer config to: ${this.configFile}`);
  }

  loadConfig() {
    try {
      if (fs.existsSync(this.configFile)) {
        const config = JSON.parse(fs.readFileSync(this.configFile, "utf8"));
        this.selectedPrinter = config.selectedPrinter || null;
        this.detectedPrinters = config.detectedPrinters || [];
        if (this.selectedPrinter?.name) {
          console.log(
            `📂 Loaded saved printer: ${this.selectedPrinter.name}`,
          );
        }
        return Boolean(this.selectedPrinter);
      }
    } catch (error) {
      console.log("⚠️ Could not load saved config:", error.message);
    }
    return false;
  }

  async setPrinterPermissions() {
    if (
      process.platform !== "linux" ||
      !this.selectedPrinter ||
      this.selectedPrinter.type !== "USB"
    ) {
      return;
    }

    try {
      const device = this.selectedPrinter.devicePath;
      console.log(`🔧 Setting permissions for ${device}…`);
      execSync(`sudo chmod 666 ${device} 2>/dev/null || true`);
      execSync(
        `sudo chown ${process.env.USER}:lp ${device} 2>/dev/null || true`,
      );
    } catch (error) {
      console.log("⚠️ Permission setting failed:", error.message);
    }
  }
}

module.exports = PrinterDetector;
