import { BadRequestException } from "@nestjs/common";
import { PostouchClientService, redondear } from "./postouch-client.service";

const config = {
  habilitada: true as const,
  url: "http://132.226.40.48:3310/create_ordenes_edngt",
  username: "22",
  password: "5551",
  computadora: "5.5",
  precioDecimales: 2,
};

const orden = {
  id: 1234,
  cliente: {
    tarjeta: "2031",
    nombre: "Juan Perez",
    nit: "CF",
    telefono: "5085-4657",
  },
  lineas: [{ plu: "2205", cantidad: 2, precio: 100.005 }],
};

describe("redondear", () => {
  it("redondea al numero de decimales indicado", () => {
    expect(redondear(100.005, 2)).toBe(100.01);
    expect(redondear(100.004, 2)).toBe(100);
    expect(redondear(100, 0)).toBe(100);
  });

  it("maneja correctamente valores afectados por IEEE-754", () => {
    expect(redondear(1.005, 2)).toBe(1.01);
    expect(redondear(35.855, 2)).toBe(35.86);
    expect(redondear(19.995, 2)).toBe(20);
    expect(redondear(2.675, 2)).toBe(2.68);
    expect(redondear(123.456, 2)).toBe(123.46);
  });
});

describe("PostouchClientService.enviarOrden", () => {
  let originalFetch: typeof fetch;

  beforeEach(() => {
    originalFetch = global.fetch;
  });

  afterEach(() => {
    global.fetch = originalFetch;
  });

  it("arma el payload con formasPago=99 por el total exacto y parsea la respuesta", async () => {
    const fetchMock = jest.fn(async () => ({
      ok: true,
      text: async () =>
        JSON.stringify({
          procesadas: 1,
          facturas: [
            { tienda: 1, caja: 2, tipo: "FAC", serie: "A", transac: 555 },
          ],
        }),
    }));
    global.fetch = fetchMock as any;

    const service = new PostouchClientService();
    const result = await service.enviarOrden(config, orden);

    expect(result).toEqual({ tienda: 1, caja: 2, tipo: "FAC", serie: "A", transac: 555 });

    const calls = fetchMock.mock.calls as any[];
    const [url, init] = calls[0];
    expect(url).toBe(config.url);
    const body = JSON.parse((init as any).body);
    expect(body._username).toBe("22");
    expect(body._password).toBe("5551");
    expect(body.computadora).toBe("5.5");
    expect(body.ordenes).toHaveLength(1);
    const [ordenEnviada] = body.ordenes;
    expect(ordenEnviada.id).toBe(1234);
    expect(ordenEnviada.productos).toEqual([
      { plu: "2205", cantidad: 2, precio: 100.01, monto: 200.02, descuentoItemPorcentaje: 0, comboNumero: 0, pluPadre: "" },
    ]);
    expect(ordenEnviada.formasPago).toEqual([
      { media: 99, emisor: 0, codigo: 0, monto: 200.02 },
    ]);
  });

  it("lanza BadRequestException si POSTouch responde con error", async () => {
    global.fetch = jest.fn(async () => ({
      ok: true,
      text: async () => JSON.stringify({ error: "No existe el PLU[2205]." }),
    })) as any;

    const service = new PostouchClientService();

    await expect(service.enviarOrden(config, orden)).rejects.toThrow(
      BadRequestException,
    );
    await expect(service.enviarOrden(config, orden)).rejects.toThrow(
      /No existe el PLU/,
    );
  });

  it("lanza BadRequestException si la respuesta HTTP no es ok", async () => {
    global.fetch = jest.fn(async () => ({
      ok: false,
      text: async () => "Internal Server Error",
    })) as any;

    const service = new PostouchClientService();

    await expect(service.enviarOrden(config, orden)).rejects.toThrow(
      BadRequestException,
    );
  });
});

describe("PostouchClientService.probarConexion", () => {
  let originalFetch: typeof fetch;

  beforeEach(() => {
    originalFetch = global.fetch;
  });

  afterEach(() => {
    global.fetch = originalFetch;
  });

  it("nunca llama a enviarOrden bajo ninguna rama -- el payload siempre lleva ordenes:[]", async () => {
    const fetchMock = jest.fn(async () => ({
      ok: true,
      text: async () => JSON.stringify({ procesadas: 0, facturas: [] }),
    }));
    global.fetch = fetchMock as any;
    const service = new PostouchClientService();

    await service.probarConexion({ url: config.url, username: "22", password: "5551", computadora: "5.5" });

    const [, init] = fetchMock.mock.calls[0] as any[];
    const body = JSON.parse(init.body);
    expect(body.ordenes).toEqual([]);
  });

  it("ok:true solo cuando la respuesta es exactamente {procesadas:0, facturas:[]}", async () => {
    global.fetch = jest.fn(async () => ({
      ok: true,
      text: async () => JSON.stringify({ procesadas: 0, facturas: [] }),
    })) as any;
    const service = new PostouchClientService();

    await expect(
      service.probarConexion({ url: config.url, username: "22", password: "5551", computadora: "5.5" }),
    ).resolves.toEqual({ ok: true });
  });

  it("ok:false si la respuesta HTTP no es exitosa", async () => {
    global.fetch = jest.fn(async () => ({ ok: false, status: 500, text: async () => "Internal Server Error" })) as any;
    const service = new PostouchClientService();

    const resultado = await service.probarConexion({
      url: config.url,
      username: "22",
      password: "5551",
      computadora: "5.5",
    });

    expect(resultado.ok).toBe(false);
  });

  it("ok:false si la respuesta trae data.error", async () => {
    global.fetch = jest.fn(async () => ({
      ok: true,
      text: async () => JSON.stringify({ error: "Usuario o contraseña incorrectos" }),
    })) as any;
    const service = new PostouchClientService();

    const resultado = await service.probarConexion({
      url: config.url,
      username: "22",
      password: "clave-mala",
      computadora: "5.5",
    });

    expect(resultado).toEqual({ ok: false, detalle: expect.stringContaining("Usuario o contraseña incorrectos") });
  });

  it("ok:false ante una forma de respuesta inesperada -- nunca asume éxito por omisión", async () => {
    global.fetch = jest.fn(async () => ({ ok: true, text: async () => JSON.stringify({ algo: "distinto" }) })) as any;
    const service = new PostouchClientService();

    const resultado = await service.probarConexion({
      url: config.url,
      username: "22",
      password: "5551",
      computadora: "5.5",
    });

    expect(resultado.ok).toBe(false);
  });

  it("ok:false si la respuesta no es JSON válido", async () => {
    global.fetch = jest.fn(async () => ({ ok: true, text: async () => "<html>error</html>" })) as any;
    const service = new PostouchClientService();

    const resultado = await service.probarConexion({
      url: config.url,
      username: "22",
      password: "5551",
      computadora: "5.5",
    });

    expect(resultado.ok).toBe(false);
  });

  it("nunca loguea la contraseña real, en ningún resultado", async () => {
    global.fetch = jest.fn(async () => ({
      ok: true,
      text: async () => JSON.stringify({ procesadas: 0, facturas: [] }),
    })) as any;
    const service = new PostouchClientService();
    const debugSpy = jest.spyOn((service as any).logger, "debug");

    await service.probarConexion({
      url: config.url,
      username: "22",
      password: "secreta-super-unica",
      computadora: "5.5",
    });

    for (const call of debugSpy.mock.calls) {
      expect(JSON.stringify(call)).not.toContain("secreta-super-unica");
    }
  });

  it("ok:false si fetch rechaza (falla de red -- DNS, conexion rechazada, timeout) -- nunca propaga la excepcion", async () => {
    global.fetch = jest.fn(async () => {
      throw new Error("ECONNREFUSED");
    }) as any;
    const service = new PostouchClientService();

    const resultado = await service.probarConexion({
      url: config.url,
      username: "22",
      password: "5551",
      computadora: "5.5",
    });

    expect(resultado).toEqual({ ok: false, detalle: expect.stringContaining("ECONNREFUSED") });
  });

  it("ok:false si la respuesta es parcialmente correcta -- guarda contra un && que se degrade a || en esRespuestaEsperada", async () => {
    const service = new PostouchClientService();

    // primer caso: procesadas correcto (0) pero facturas no vacio
    global.fetch = jest.fn(async () => ({
      ok: true,
      text: async () => JSON.stringify({ procesadas: 0, facturas: [{ tienda: 1 }] }),
    })) as any;
    const resultado1 = await service.probarConexion({
      url: config.url,
      username: "22",
      password: "5551",
      computadora: "5.5",
    });
    expect(resultado1.ok).toBe(false);

    // segundo caso: facturas vacio (correcto) pero procesadas distinto de 0
    global.fetch = jest.fn(async () => ({
      ok: true,
      text: async () => JSON.stringify({ procesadas: 3, facturas: [] }),
    })) as any;
    const resultado2 = await service.probarConexion({
      url: config.url,
      username: "22",
      password: "5551",
      computadora: "5.5",
    });
    expect(resultado2.ok).toBe(false);
  });
});
