import { ConflictException } from '@nestjs/common';
import { EcommercePedidosService } from './ecommerce-pedidos.service';

function buildService(updateCount: number) {
  const session = {
    id: 'session-1',
    empresa_id: 'empresa-1',
    codigo: 'ECOM-2026-0001',
    estado: 'pago_confirmado',
    pedido_id: 'pedido-1',
    delivery_snapshot: { type: 'pickup' },
    workflow_snapshot: {
      preset: 'completo',
      verificationEnabled: true,
      packingEnabled: true,
      transitEnabled: true,
    },
    customer_snapshot: {},
    totals_snapshot: {},
    items_snapshot: [],
    metodo_pago: 'manual',
  };
  const tx = {
    ecommerce_checkout_session: {
      updateMany: jest.fn().mockResolvedValue({ count: updateCount }),
    },
    ecommerce_pedido_evento: {
      create: jest.fn().mockResolvedValue({}),
    },
  };
  const prisma: any = {
    ecommerce_checkout_session: {
      findFirst: jest.fn().mockResolvedValue(session),
      findUnique: jest.fn().mockResolvedValue({ ...session, estado: 'en_verificacion' }),
    },
    ecommerce_config: { findUnique: jest.fn() },
    $transaction: jest.fn().mockImplementation((callback: any) => callback(tx)),
    // attachFacturas: sin factura emitida para el pedido.
    $queryRaw: jest.fn().mockResolvedValue([]),
  };
  const service = new EcommercePedidosService(
    prisma,
    {} as any,
    { onOrderShipped: jest.fn(), onOrderCancelled: jest.fn() } as any,
    {} as any,
  );
  return { service, tx };
}

describe('EcommercePedidosService concurrencia', () => {
  it('toma el pedido y registra el operador en una sola transición atómica', async () => {
    const { service, tx } = buildService(1);

    const result = await service.changeEstado('empresa-1', 'session-1', 'en_verificacion', undefined, 'usuario-1');

    expect(tx.ecommerce_checkout_session.updateMany).toHaveBeenCalledWith(
      expect.objectContaining({
        where: expect.objectContaining({ estado: 'pago_confirmado' }),
        data: expect.objectContaining({ operador_asignado_id: 'usuario-1' }),
      }),
    );
    expect(tx.ecommerce_pedido_evento.create).toHaveBeenCalled();
    expect(result.estado).toBe('en_verificacion');
  });

  it('rechaza al segundo operador cuando el estado ya cambió', async () => {
    const { service, tx } = buildService(0);

    await expect(
      service.changeEstado('empresa-1', 'session-1', 'en_verificacion', undefined, 'usuario-2'),
    ).rejects.toBeInstanceOf(ConflictException);
    expect(tx.ecommerce_pedido_evento.create).not.toHaveBeenCalled();
  });
});

describe('EcommercePedidosService.crearPedidoErp', () => {
  function armar(totals: any, checkoutConfig: any = {}, customer: any = { name: 'QA' }) {
    const session = {
      id: 's1',
      empresa_id: 'e1',
      codigo: 'ECOM-2026-0099',
      pedido_id: null,
      items_snapshot: [{ id: 'prod-1', quantity: 2, unitPrice: 25650 }],
      customer_snapshot: customer,
      delivery_snapshot: { type: 'delivery', methodName: 'Delivery programado' },
      totals_snapshot: totals,
    };
    const prisma: any = {
      ecommerce_checkout_session: { findFirst: jest.fn().mockResolvedValue(session), update: jest.fn() },
      ecommerce_config: {
        findUnique: jest.fn().mockResolvedValue({ published_version_id: null, checkout_config: checkoutConfig }),
      },
      clientes: {
        findFirst: jest.fn().mockImplementation(({ where }: any) =>
          Promise.resolve(where.persona_id ? { id: 'cli-comprador' } : { id: 'cli-generico', persona_id: 'per-gen', personas: { naturaleza_id: null } }),
        ),
      },
      personas: {
        findFirst: jest.fn().mockResolvedValue({ id: 'per-comprador' }),
        update: jest.fn().mockResolvedValue({}),
      },
      naturaleza_receptor: { findFirst: jest.fn().mockResolvedValue({ id: 'nat-2' }) },
      tipo_documento_identidad: { findFirst: jest.fn().mockResolvedValue({ id: 'doc-innominado' }) },
      paises: { findFirst: jest.fn().mockResolvedValue({ id: 'pry' }) },
      tipo_operacion: { findFirst: jest.fn().mockResolvedValue({ id: 'b2c' }) },
    };
    const pedidosService = {
      create: jest.fn().mockResolvedValue({ data: { id: 'ped-1' } }),
      confirmar: jest.fn().mockResolvedValue({}),
    };
    const service = new EcommercePedidosService(prisma, pedidosService as any, {} as any, { liberar: jest.fn() } as any);
    return { service, pedidosService, prisma };
  }

  it('lleva el descuento por medio de pago y el envío al pedido ERP', async () => {
    const { service, pedidosService } = armar(
      { subtotal: 51300, paymentDiscount: 5130, deliveryCost: 18000, total: 64170 },
      { envioProductoId: 'prod-envio' },
    );
    await service.crearPedidoErp('e1', 's1', 'u1');
    const dto = pedidosService.create.mock.calls[0][0];
    expect(dto.items).toEqual([
      { producto_id: 'prod-1', cantidad: 2, precio_negociado: 25650, descuento_porcentaje: 10 },
      { producto_id: 'prod-envio', cantidad: 1, precio_negociado: 18000, observacion: 'Delivery programado' },
    ]);
  });

  it('sin producto de envío deja el aviso en las observaciones', async () => {
    const { service, pedidosService } = armar({ subtotal: 25650, paymentDiscount: 0, deliveryCost: 18000 });
    await service.crearPedidoErp('e1', 's1', 'u1');
    const dto = pedidosService.create.mock.calls[0][0];
    expect(dto.items).toHaveLength(1);
    expect(dto.items[0]).not.toHaveProperty('descuento_porcentaje');
    expect(dto.observaciones).toContain('envío cobrado 18000 sin producto de envío');
  });

  it('factura a nombre del comprador si cargó su documento', async () => {
    const { service, pedidosService } = armar({ subtotal: 25650 }, {}, { name: 'Juan', ruc: '4567890', consultadoSIFEN: false });
    await service.crearPedidoErp('e1', 's1', 'u1');
    expect(pedidosService.create.mock.calls[0][0].cliente_id).toBe('cli-comprador');
  });

  it('sin documento usa el consumidor final y le completa los datos fiscales', async () => {
    const { service, pedidosService, prisma } = armar({ subtotal: 25650 });
    await service.crearPedidoErp('e1', 's1', 'u1');
    expect(pedidosService.create.mock.calls[0][0].cliente_id).toBe('cli-generico');
    expect(prisma.personas.update).toHaveBeenCalledWith({
      where: { id: 'per-gen' },
      data: { naturaleza_id: 'nat-2', tipo_documento_id: 'doc-innominado', nro_documento: '0', pais_id: 'pry' },
    });
  });
});
