import { NotFoundException } from '@nestjs/common';
import { PersonasService } from './personas.service';

/**
 * `PUT /clientes/:id` devolvía 500 ante cualquier edición parcial.
 *
 * `update` arrancaba con `naturaleza_receptor.findUnique({ where: { id:
 * updatePersonaDto.naturaleza_id } })`. Si el payload no traía `naturaleza_id`,
 * ese `id` era `undefined` y Prisma rechaza un `findUnique` sin ningún campo
 * definido. El formulario de clientes nunca lo vio porque reenvía todos los
 * campos; se descubrió llamando al endpoint con sólo `tipo_operacion_id`.
 *
 * **Lo que esto arregla y lo que no.** Ya no hay 500: la naturaleza se toma de la
 * persona guardada. Pero el endpoint **sigue necesitando los datos fiscales
 * completos**, porque `validatePersonaInput` valida el payload y no la entidad
 * resultante. Hacer que la edición parcial funcione de verdad es rehacer esa
 * validación sobre la mezcla de lo guardado y lo enviado, y eso es un cambio del
 * módulo de clientes que no corresponde a este trabajo. Lo que cambia hoy es que
 * el error dice qué falta en vez de reventar.
 */
describe('PersonasService.update — edición parcial', () => {
  const PERSONA = {
    id: 'per-1', empresa_id: 'emp-1', deleted: false,
    naturaleza_id: 'nat-no-contrib', razon_social: 'Spa Aguas Claras',
  };

  const crear = (over: Record<string, any> = {}) => {
    const prisma: any = {
      personas: {
        findUnique: jest.fn().mockResolvedValue(PERSONA),
        findFirst: jest.fn().mockResolvedValue(null),
        update: jest.fn().mockImplementation(({ data }) => Promise.resolve({ id: 'per-1', ...data })),
      },
      naturaleza_receptor: { findUnique: jest.fn().mockResolvedValue({ id: 'nat-no-contrib', codigo: 2 }) },
      tipo_documento_identidad: { findUnique: jest.fn().mockResolvedValue({ codigo: 1 }) },
      tipo_contribuyente: { findUnique: jest.fn().mockResolvedValue({ id: 'tc-1' }) },
      ciudad: { findUnique: jest.fn().mockResolvedValue({ id: 'ciu-1' }) },
      ...over,
    };
    return { service: new PersonasService(prisma), prisma };
  };

  it('sin naturaleza_id usa la que ya tiene la persona', async () => {
    const { service, prisma } = crear();

    // Los datos fiscales sí hay que mandarlos: ver la nota del encabezado.
    await service.update(
      'per-1',
      { razon_social: 'Spa Aguas Claras', tipo_documento_id: 'doc-ci', nro_documento: '9999103' } as never,
      'emp-1',
    );

    expect(prisma.naturaleza_receptor.findUnique).toHaveBeenCalledWith({ where: { id: 'nat-no-contrib' } });
    expect(prisma.personas.update.mock.calls[0][0].data.naturaleza_id).toBe('nat-no-contrib');
  });

  /** Nunca más un `findUnique` con `id: undefined`, que es lo que daba el 500. */
  it('nunca consulta la naturaleza con id undefined', async () => {
    const { service, prisma } = crear();
    // Incluso con el payload más pobre posible, que antes era el que reventaba.
    await service.update('per-1', {} as never, 'emp-1').catch(() => undefined);
    expect(prisma.naturaleza_receptor.findUnique).toHaveBeenCalled();
    for (const llamada of prisma.naturaleza_receptor.findUnique.mock.calls) {
      expect(llamada[0].where.id).toBeDefined();
    }
  });

  /**
   * El contrato real de hoy: con un payload incompleto el error es claro y
   * accionable, no un 500 opaco.
   */
  it('un payload incompleto da un error que dice qué falta', async () => {
    const { service } = crear();
    await expect(service.update('per-1', {} as never, 'emp-1')).rejects.toThrow(
      /tipo de documento de identidad es obligatorio/i,
    );
  });

  it('un naturaleza_id explícito le gana a la guardada', async () => {
    const { service, prisma } = crear({
      naturaleza_receptor: { findUnique: jest.fn().mockResolvedValue({ id: 'nat-contrib', codigo: 1 }) },
    });
    await service.update('per-1', { naturaleza_id: 'nat-contrib', ruc: '80012345', dv: 6, tipo_contribuyente_id: 'tc-1' } as never, 'emp-1');
    expect(prisma.personas.update.mock.calls[0][0].data.naturaleza_id).toBe('nat-contrib');
  });

  it('la persona tiene que existir y no estar borrada', async () => {
    const { service } = crear({ personas: { findUnique: jest.fn().mockResolvedValue(null) } });
    await expect(service.update('per-x', {} as never, 'emp-1')).rejects.toThrow(NotFoundException);
  });

  it('si la persona no tiene naturaleza y tampoco viene, lo dice claro', async () => {
    const { service } = crear({
      personas: {
        findUnique: jest.fn().mockResolvedValue({ ...PERSONA, naturaleza_id: null }),
        findFirst: jest.fn().mockResolvedValue(null),
        update: jest.fn(),
      },
    });
    await expect(service.update('per-1', {} as never, 'emp-1')).rejects.toThrow(
      /no tiene naturaleza/i,
    );
  });

  it('una naturaleza inexistente sigue siendo 404', async () => {
    const { service } = crear({ naturaleza_receptor: { findUnique: jest.fn().mockResolvedValue(null) } });
    await expect(service.update('per-1', { naturaleza_id: 'nat-x' } as never, 'emp-1')).rejects.toThrow(
      NotFoundException,
    );
  });
});
