import { FacturacionPosService } from "./facturacion-pos.service";

function createTallerParametrosMock(overrides: Record<string, any> = {}) {
  const valores: Record<string, any> = {
    "facturacion.usa_facturacion": true,
    "facturacion_pos.habilitada": true,
    "facturacion_pos.url": "http://postouch/create_ordenes_edngt",
    "facturacion_pos.username": "22",
    "facturacion_pos.password": "5551",
    "facturacion_pos.computadora": "5.5",
    "facturacion_pos.precio_decimales": 2,
    ...overrides.valores,
  };

  return {
    getBooleanConFallback: jest.fn(async (clave: string) => Boolean(valores[clave])),
    getParaTienda: jest.fn(async (_tiendaId: number, clave: string) => valores[clave]),
    ...overrides.tallerParametros,
  };
}

function createService(overrides: Record<string, any> = {}) {
  const otFacturacionRepository = {
    save: jest.fn(async (data: any) => ({ id: 1, ...data })),
    ...overrides.otFacturacionRepository,
  };

  const tenantAccessor = {
    getCurrentTenantId: jest.fn(() => 42),
    repositoryFor: jest.fn(() => otFacturacionRepository),
    ...overrides.tenantAccessor,
  };

  const maepluMirrorService = {
    insertIfMissing: jest.fn(async () => ({ inserted: true })),
    ...overrides.maepluMirrorService,
  };

  const postouchClientService = {
    enviarOrden: jest.fn(async () => ({
      tienda: 1,
      caja: 2,
      tipo: "FAC",
      serie: "A",
      transac: 555,
    })),
    ...overrides.postouchClientService,
  };

  const tallerParametros = createTallerParametrosMock(overrides);

  const service = new FacturacionPosService(
    tenantAccessor as any,
    maepluMirrorService as any,
    postouchClientService as any,
    tallerParametros as any,
  );

  return {
    service,
    tenantAccessor,
    tallerParametros,
    maepluMirrorService,
    postouchClientService,
    otFacturacionRepository,
  };
}

const otBase = {
  id: 7,
  numeroOt: "OT-1234",
  tiendaId: 3,
  clienteTarjeta: "2031",
  cliente: { nombre: "Juan Perez", nit: "CF", telefono: "5085-4657" },
  cotizacion: {
    lineas: [
      { plu: "2205", descripcion: "Aceite", precioUnitario: 100, cantidad: 2, esServicio: false },
      { plu: "TLLP-00000001", descripcion: "Mano de obra", precioUnitario: 250, cantidad: 1, esServicio: true, origenLocal: true },
    ],
  },
};

describe("FacturacionPosService.enviarOrdenTerminada", () => {
  it("retorna exito:false sin llamar a POSTouch si facturacion.usa_facturacion esta apagado", async () => {
    const { service, postouchClientService } = createService({
      valores: { "facturacion.usa_facturacion": false },
    });

    const result = await service.enviarOrdenTerminada(otBase);

    expect(result).toEqual({
      exito: false,
      error: "Facturación automática desactivada para este tenant -- Configuración → Facturación.",
    });
    expect(postouchClientService.enviarOrden).not.toHaveBeenCalled();
  });

  it("retorna exito:false sin llamar a POSTouch si la OT no tiene tienda asignada", async () => {
    const { service, postouchClientService } = createService();

    const result = await service.enviarOrdenTerminada({ ...otBase, tiendaId: null });

    expect(result).toEqual({ exito: false, error: "OT sin tienda asignada, no se puede facturar por POS" });
    expect(postouchClientService.enviarOrden).not.toHaveBeenCalled();
  });

  it("retorna exito:false sin llamar a POSTouch si la tienda no tiene facturacion habilitada", async () => {
    const { service, postouchClientService } = createService({
      valores: { "facturacion_pos.habilitada": false },
    });

    const result = await service.enviarOrdenTerminada(otBase);

    expect(result).toEqual({ exito: false, error: "Facturacion POS no habilitada para esta tienda" });
    expect(postouchClientService.enviarOrden).not.toHaveBeenCalled();
  });

  it("espeja los productos locales antes de enviar, y guarda la referencia al facturar con exito", async () => {
    const { service, maepluMirrorService, postouchClientService, otFacturacionRepository, tallerParametros } =
      createService();

    const result = await service.enviarOrdenTerminada(otBase);

    expect(result).toEqual({ exito: true });
    expect(tallerParametros.getParaTienda).toHaveBeenCalledWith(3, "facturacion_pos.password");
    expect(maepluMirrorService.insertIfMissing).toHaveBeenCalledTimes(1);
    expect(maepluMirrorService.insertIfMissing).toHaveBeenCalledWith({
      plu: "TLLP-00000001",
      desclarga: "Mano de obra",
      precio: 250,
      costo: 0,
      iva: 0,
      pagaiva: true,
      esServicio: true,
      usainventario: false,
    });
    expect(postouchClientService.enviarOrden).toHaveBeenCalledWith(
      expect.objectContaining({ url: "http://postouch/create_ordenes_edngt" }),
      expect.objectContaining({
        id: 7,
        cliente: { tarjeta: "2031", nombre: "Juan Perez", nit: "CF", telefono: "5085-4657" },
        lineas: [
          { plu: "2205", cantidad: 2, precio: 100 },
          { plu: "TLLP-00000001", cantidad: 1, precio: 250 },
        ],
      }),
    );
    expect(otFacturacionRepository.save).toHaveBeenCalledWith(
      expect.objectContaining({
        otId: 7,
        tienda: 1,
        caja: 2,
        tipo: "FAC",
        serie: "A",
        transac: 555,
      }),
    );
  });

  it("retorna exito:false con el mensaje de error si falla la resolucion de contexto de tenant", async () => {
    const { service, postouchClientService, otFacturacionRepository } = createService({
      tallerParametros: {
        getParaTienda: jest.fn(() => {
          throw new Error("No hay contexto de tenant resuelto");
        }),
      },
    });

    const result = await service.enviarOrdenTerminada(otBase);

    expect(result).toEqual({ exito: false, error: "No hay contexto de tenant resuelto" });
    expect(postouchClientService.enviarOrden).not.toHaveBeenCalled();
    expect(otFacturacionRepository.save).not.toHaveBeenCalled();
  });

  it("retorna exito:false con el mensaje de error si la config de la tienda esta incompleta", async () => {
    const { service, postouchClientService, otFacturacionRepository } = createService({
      valores: { "facturacion_pos.url": null },
    });

    const result = await service.enviarOrdenTerminada(otBase);

    expect(result).toEqual({
      exito: false,
      error: "La tienda 3 tiene facturacionPosHabilitada pero no tiene facturacionPosUrl configurada",
    });
    expect(postouchClientService.enviarOrden).not.toHaveBeenCalled();
    expect(otFacturacionRepository.save).not.toHaveBeenCalled();
  });

  it("retorna exito:false con el mensaje de error si POSTouch rechaza la orden", async () => {
    const { service, otFacturacionRepository } = createService({
      postouchClientService: {
        enviarOrden: jest.fn(async () => {
          throw new Error("POSTouch rechazo la orden: No existe el PLU[2205].");
        }),
      },
    });

    const result = await service.enviarOrdenTerminada(otBase);

    expect(result).toEqual({
      exito: false,
      error: "POSTouch rechazo la orden: No existe el PLU[2205].",
    });
    expect(otFacturacionRepository.save).not.toHaveBeenCalled();
  });
});
