/**
 * Servicio: archiveReader
 *
 * Lista el contenido de contenedores comunes (ZIP, 7z) sin necesidad de
 * extraerlos completamente. Intenta usar la utilidad `7z` (7-Zip) con la
 * opción `-slt` para obtener un listado con metadatos; si falla y el archivo
 * es ZIP, intenta usar `unzip -l` como fallback.
 *
 * La función principal exportada es `listArchiveEntries(filePath, logger)` y
 * devuelve un array de objetos `{ name, size }` con las entradas encontradas.
 *
 * Nota: este servicio depende de binarios del sistema (`7z`, `unzip`) cuando
 * se trabaja con formatos comprimidos. En entornos de CI/producción debe
 * asegurarse la presencia de dichas utilidades o los tests deben mockear
 * las llamadas a `child_process.exec`.
 */
import path from 'path';
import { exec } from 'child_process';

export type ArchiveEntry = { name: string; size: number };

export async function listArchiveEntries(
  filePath: string,
  logger: { warn?: (...args: any[]) => void } = console
): Promise<ArchiveEntry[]> {
  const ext = path.extname(filePath).toLowerCase().replace(/^\./, '');

  if (!['zip', '7z'].includes(ext)) return [];

  const execCmd = (cmd: string) =>
    new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {
      exec(cmd, (err, stdout, stderr) => {
        if (err) return reject(err);
        resolve({ stdout: String(stdout), stderr: String(stderr) });
      });
    });

  // Intentamos 7z -slt (salida técnica fácil de parsear)
  const try7z = async () => {
    const { stdout } = await execCmd(`7z l -slt ${JSON.stringify(filePath)}`);
    const blocks = String(stdout).split(/\r?\n\r?\n/);
    const entries: ArchiveEntry[] = [];

    for (const block of blocks) {
      const lines = block.split(/\r?\n/);
      const pathLine = lines.find((l) => l.startsWith('Path = '));
      if (!pathLine) continue;
      const name = pathLine.replace(/^Path = /, '').trim();
      const sizeLine = lines.find((l) => l.startsWith('Size = '));
      const size = sizeLine ? parseInt(sizeLine.replace(/^Size = /, ''), 10) || 0 : 0;
      entries.push({ name, size });
    }

    return entries;
  };

  try {
    return await try7z();
  } catch (err) {
    logger.warn?.({ err, filePath }, 'archiveReader: 7z failed, attempting fallback');

    if (ext === 'zip') {
      try {
        const { stdout } = await execCmd(`unzip -l ${JSON.stringify(filePath)}`);
        const lines = String(stdout).split(/\r?\n/);
        const entries: ArchiveEntry[] = [];

        for (const line of lines) {
          // línea típica: "   12345  path/to/file.bin"
          const m = line.match(/^\s*(\d+)\s+(.+)$/);
          if (m) {
            const size = parseInt(m[1], 10);
            const name = m[2].trim();
            entries.push({ name, size });
          }
        }

        return entries;
      } catch (err2) {
        logger.warn?.({ err2, filePath }, 'archiveReader: unzip fallback failed');
        return [];
      }
    }

    return [];
  }
}

export default { listArchiveEntries };
