import { execSync, spawnSync } from "child_process";
import * as path from "path";

// Motivo de esta prueba: el 12/08 un despliegue real fallo con
// "Cannot find module '/app/dist/main.js'" porque agregar
// backend/scripts/*.ts (fuera de src/) le cambio a tsc el rootDir inferido —
// dist/main.js termino en dist/src/main.js. NINGUNA verificacion existente
// lo detecto: tsc --noEmit pasaba (typecheckea, no verifica RUTA de salida),
// `nest build` decia "compilo" sin error, y las 75 pruebas unitarias corren
// contra el codigo FUENTE (ts-jest), nunca contra el artefacto compilado ni
// la imagen real. Esta prueba es la unica que corre contra la imagen de
// Docker de verdad — la misma que se despliega.
//
// Se salta entera (nunca falla) si el daemon de Docker no esta disponible en
// el entorno que corre los tests — `docker --version` solo prueba que el
// CLI este instalado, no que el daemon responda (ej. en un sandbox sin
// Docker Desktop corriendo). Preferible un salto silencioso y explicito a un
// falso rojo en un entorno que nunca podria correr esto.
function dockerDaemonDisponible(): boolean {
  try {
    execSync("docker info", { stdio: "ignore", timeout: 10000 });
    return true;
  } catch {
    return false;
  }
}

const DOCKER_OK = dockerDaemonDisponible();
const describeSiHayDocker = DOCKER_OK ? describe : describe.skip;

if (!DOCKER_OK) {
  // eslint-disable-next-line no-console
  console.warn(
    "[docker-image-boots] Docker no disponible en este entorno (CLI presente pero el daemon no responde, " +
      "o Docker no esta instalado) — se salta esta prueba. Correrla en un entorno con Docker real antes de " +
      "confiar en un despliegue.",
  );
}

describeSiHayDocker("la imagen de produccion del backend arranca de verdad (e2e)", () => {
  const BACKEND_DIR = path.join(__dirname, "..");
  const IMAGE_TAG = "tll-backend-build-check:e2e-test";

  beforeAll(() => {
    execSync(`docker build -t ${IMAGE_TAG} --target production -f Dockerfile .`, {
      cwd: BACKEND_DIR,
      stdio: "pipe",
    });
  }, 300000);

  afterAll(() => {
    try {
      execSync(`docker rmi -f ${IMAGE_TAG}`, { stdio: "ignore" });
    } catch {
      // No es motivo de falla que la limpieza de la imagen de prueba falle.
    }
  }, 30000);

  it("dist/main.js existe en la imagen construida (el bug real: quedaba en dist/src/main.js)", () => {
    const result = spawnSync("docker", ["run", "--rm", IMAGE_TAG, "test", "-f", "/app/dist/main.js"], {
      encoding: "utf8",
    });
    expect(result.status).toBe(0);
  }, 30000);

  // No usa credenciales reales a proposito: el punto no es levantar contra
  // una base de verdad (eso lo cubre el resto del e2e), es probar que el
  // proceso de Node ARRANCA y ejecuta main.js sin morir por un problema de
  // empaquetado. Con host/credenciales inventados, el preflight (ver
  // src/preflight.ts) va a fallar — y ESO es la señal correcta: si aparece
  // "ARRANQUE ABORTADO" (el mensaje propio del preflight), el modulo se
  // encontro y se ejecuto. Si en cambio aparece "Cannot find module" o
  // "MODULE_NOT_FOUND", es el bug de rootDir/build de vuelta.
  it("el contenedor ejecuta main.js y llega al preflight (no MODULE_NOT_FOUND)", () => {
    const result = spawnSync(
      "docker",
      [
        "run", "--rm",
        "-e", "TENANT_CLI_DB_HOST=host-inexistente.invalid",
        "-e", "TENANT_CLI_DB_PORT=3306",
        "-e", "TENANT_CLI_DB_DATABASE=fake",
        "-e", "TENANT_CLI_DB_USERNAME=fake",
        "-e", "TENANT_CLI_DB_PASSWORD=fake",
        "-e", "CONTROL_PLANE_DB_HOST=host-inexistente.invalid",
        "-e", "CONTROL_PLANE_DB_PORT=3306",
        "-e", "CONTROL_PLANE_DB_DATABASE=fake",
        "-e", "CONTROL_PLANE_DB_USERNAME=fake",
        "-e", "CONTROL_PLANE_DB_PASSWORD=fake",
        "-e", "JWT_SECRET=solo-para-esta-prueba-de-build",
        "-e", "CONTROL_PLANE_CREDENTIALS_ENCRYPTION_KEY=solo-para-esta-prueba-de-build-16",
        IMAGE_TAG,
      ],
      { encoding: "utf8", timeout: 30000 },
    );

    const output = `${result.stdout || ""}${result.stderr || ""}`;
    expect(output).not.toMatch(/Cannot find module/i);
    expect(output).not.toMatch(/MODULE_NOT_FOUND/);
    expect(output).toContain("ARRANQUE ABORTADO");
  }, 30000);
});
