// Busca impresoras termicas en la red del local: prueba el puerto 9100 (el
// estandar ESC/POS) en cada direccion de las subredes /24 de la laptop.
import { createConnection } from 'node:net';
import { networkInterfaces } from 'node:os';

function puertoAbierto(host: string, puerto: number, timeoutMs = 400): Promise<boolean> {
  return new Promise((resolve) => {
    const sock = createConnection({ host, port: puerto });
    let listo = false;
    const fin = (ok: boolean) => {
      if (listo) return;
      listo = true;
      sock.destroy();
      resolve(ok);
    };
    sock.setTimeout(timeoutMs, () => fin(false));
    sock.on('connect', () => fin(true));
    sock.on('error', () => fin(false));
  });
}

/** Primera IPv4 privada de la laptop (la direccion que usan las tablets). */
export function ipLocal(): string | null {
  for (const lista of Object.values(networkInterfaces())) {
    for (const inf of lista ?? []) {
      if (inf.family === 'IPv4' && !inf.internal) return inf.address;
    }
  }
  return null;
}

/** Subredes /24 privadas de la laptop (una base "192.168.1." por cada red). */
function basesLocales(): string[] {
  const bases = new Set<string>();
  for (const lista of Object.values(networkInterfaces())) {
    for (const inf of lista ?? []) {
      if (inf.family !== 'IPv4' || inf.internal) continue;
      const [a, b, c] = inf.address.split('.');
      const na = Number(a);
      const esPrivada = na === 10 || na === 192 || (na === 172 && Number(b) >= 16 && Number(b) <= 31);
      if (esPrivada) bases.add(`${a}.${b}.${c}.`);
    }
  }
  return [...bases];
}

/** Escanea la red y devuelve las direcciones que responden como impresora (9100). */
export async function buscarImpresoras(
  puerto = 9100,
  alProgresar?: (hechas: number, total: number) => void,
): Promise<string[]> {
  const objetivos: string[] = [];
  for (const base of basesLocales()) {
    for (let i = 1; i <= 254; i++) objetivos.push(base + i);
  }
  const encontradas: string[] = [];
  let hechas = 0;
  const LOTE = 60;
  for (let i = 0; i < objetivos.length; i += LOTE) {
    const lote = objetivos.slice(i, i + LOTE);
    const res = await Promise.all(lote.map((ip) => puertoAbierto(ip, puerto)));
    res.forEach((ok, j) => {
      if (ok) encontradas.push(lote[j]);
    });
    hechas += lote.length;
    alProgresar?.(hechas, objetivos.length);
  }
  return encontradas;
}
