import { BadRequestException, NotFoundException } from '@nestjs/common';
import { CrmPruebaService } from './crm-prueba.service';

const d = (iso: string) => new Date(`${iso}T00:00:00.000Z`);

describe('CrmPruebaService', () => {
  const SUSCRIPCION = { id: 'sus-1', limite_override: null };

  const crear = (overrides: Record<string, any> = {}) => {
    const prisma: any = {
      suscripciones: { findFirst: jest.fn().mockResolvedValue(SUSCRIPCION) },
      submodulos: {
        findFirst: jest.fn().mockResolvedValue({ id: 'sub-1', codigo: 'CRM_CONTRATOS', dias_prueba: 30 }),
        findMany: jest.fn().mockResolvedValue([]),
      },
      suscripcion_submodulos: {
        findFirst: jest.fn().mockResolvedValue(null),
        findMany: jest.fn().mockResolvedValue([]),
        create: jest.fn().mockImplementation(({ data }) => Promise.resolve({ id: 'ss-1', ...data })),
        update: jest.fn().mockImplementation(({ data }) => Promise.resolve({ id: 'ss-1', ...data })),
      },
      ...overrides,
    };
    const alertas = { registrar: jest.fn().mockResolvedValue({ id: 'al-1', creada: true }) };
    return { service: new CrmPruebaService(prisma as any, alertas as any), prisma, alertas };
  };

  describe('iniciarPrueba', () => {
    it('toma los días del submódulo cuando no hay override', async () => {
      const { service, prisma } = crear();

      const r = await service.iniciarPrueba('emp-1', 'CRM_CONTRATOS');

      expect(r.dias).toBe(30);
      expect(prisma.suscripcion_submodulos.create).toHaveBeenCalled();
      const data = prisma.suscripcion_submodulos.create.mock.calls[0][0].data;
      expect(data.suscripcion_id).toBe('sus-1');
      expect(data.fecha_fin_prueba).toBeInstanceOf(Date);
    });

    it('el override por cliente le gana al default del submódulo', async () => {
      const { service } = crear({
        suscripciones: {
          findFirst: jest
            .fn()
            .mockResolvedValue({ id: 'sus-1', limite_override: { dias_prueba: { CRM_CONTRATOS: 60 } } }),
        },
      });

      const r = await service.iniciarPrueba('emp-1', 'CRM_CONTRATOS');

      expect(r.dias).toBe(60);
    });

    it('ignora un override inválido y cae al default', async () => {
      const { service } = crear({
        suscripciones: {
          findFirst: jest
            .fn()
            .mockResolvedValue({ id: 'sus-1', limite_override: { dias_prueba: { CRM_CONTRATOS: -5 } } }),
        },
      });

      expect((await service.iniciarPrueba('emp-1', 'CRM_CONTRATOS')).dias).toBe(30);
    });

    it('rechaza un submódulo que no ofrece prueba', async () => {
      const { service } = crear({
        submodulos: {
          findFirst: jest.fn().mockResolvedValue({ id: 'sub-1', codigo: 'CRM_CONFIG', dias_prueba: null }),
        },
      });

      await expect(service.iniciarPrueba('emp-1', 'CRM_CONFIG')).rejects.toThrow(BadRequestException);
    });

    it('no deja repetir la prueba: sería regalar meses gratis', async () => {
      const { service } = crear({
        suscripcion_submodulos: {
          findFirst: jest.fn().mockResolvedValue({ id: 'ss-1', fecha_fin_prueba: d('2026-01-01') }),
          create: jest.fn(),
          update: jest.fn(),
        },
      });

      await expect(service.iniciarPrueba('emp-1', 'CRM_CONTRATOS')).rejects.toThrow(BadRequestException);
    });

    it('exige suscripción vigente', async () => {
      const { service } = crear({ suscripciones: { findFirst: jest.fn().mockResolvedValue(null) } });

      await expect(service.iniciarPrueba('emp-1', 'CRM_CONTRATOS')).rejects.toThrow(NotFoundException);
    });
  });

  describe('estadoCrm', () => {
    it('deriva el estado de cada submódulo y marca activo al que no está en prueba', async () => {
      const { service } = crear({
        submodulos: {
          findMany: jest.fn().mockResolvedValue([
            { id: 'sub-1', codigo: 'CRM_CONFIG', descripcion: 'Configuración CRM' },
            { id: 'sub-2', codigo: 'CRM_CONTRATOS', descripcion: 'Contratos' },
          ]),
        },
        suscripcion_submodulos: {
          findMany: jest.fn().mockResolvedValue([
            { submodulo_id: 'sub-1', fecha_fin_prueba: null, active: true },
            { submodulo_id: 'sub-2', fecha_fin_prueba: d('2099-01-01'), active: true },
          ]),
        },
      });

      const estados = await service.estadoCrm('emp-1');

      expect(estados.find((e) => e.codigo === 'CRM_CONFIG')?.estado).toBe('activo');
      expect(estados.find((e) => e.codigo === 'CRM_CONTRATOS')?.estado).toBe('prueba');
    });

    it('una fila inactiva no cuenta como prueba vigente', async () => {
      const { service } = crear({
        submodulos: {
          findMany: jest.fn().mockResolvedValue([{ id: 'sub-1', codigo: 'CRM_CONFIG', descripcion: 'Config' }]),
        },
        suscripcion_submodulos: {
          findMany: jest.fn().mockResolvedValue([{ submodulo_id: 'sub-1', fecha_fin_prueba: d('2020-01-01'), active: false }]),
        },
      });

      expect((await service.estadoCrm('emp-1'))[0].estado).toBe('activo');
    });
  });

  describe('avisosDiarios', () => {
    const fila = (fin: Date) => ({
      fecha_fin_prueba: fin,
      submodulos: { codigo: 'CRM_CONTRATOS', descripcion: 'Contratos' },
      suscripciones: { empresa_id: 'emp-1', estado: 'Activo' },
    });

    it('avisa con criticidad creciente según se acerca el vencimiento', async () => {
      const hoy = d('2026-10-01');
      const { service, alertas } = crear({
        suscripcion_submodulos: {
          findMany: jest.fn().mockResolvedValue([fila(d('2026-10-08')), fila(d('2026-10-02'))]),
        },
      });

      await service.avisosDiarios(hoy);

      const criticidades = alertas.registrar.mock.calls.map((c: any[]) => c[0].criticidad);
      expect(criticidades).toEqual(['baja', 'alta']); // 7 días y 1 día
    });

    it('la clave del aviso distingue estado y días: no se tapan entre sí', async () => {
      const hoy = d('2026-10-01');
      const { service, alertas } = crear({
        suscripcion_submodulos: { findMany: jest.fn().mockResolvedValue([fila(d('2026-10-04'))]) },
      });

      await service.avisosDiarios(hoy);

      expect(alertas.registrar.mock.calls[0][0].clave).toBe('prueba:CRM_CONTRATOS:prueba:3');
    });

    it('avisa el día que pasa a solo lectura', async () => {
      const hoy = d('2026-10-01');
      const { service, alertas } = crear({
        suscripcion_submodulos: { findMany: jest.fn().mockResolvedValue([fila(d('2026-09-30'))]) },
      });

      await service.avisosDiarios(hoy);

      const alerta = alertas.registrar.mock.calls[0][0];
      expect(alerta.clave).toContain(':solo_lectura:');
      expect(alerta.descripcion).toContain('solo lectura');
    });

    it('avisa el día que se bloquea', async () => {
      const hoy = d('2026-11-01');
      const { service, alertas } = crear({
        suscripcion_submodulos: { findMany: jest.fn().mockResolvedValue([fila(d('2026-09-30'))]) },
      });

      await service.avisosDiarios(hoy);

      expect(alertas.registrar.mock.calls[0][0].clave).toContain(':bloqueado:');
    });

    it('no hace nada si no hay pruebas por vencer', async () => {
      const { service, alertas } = crear();

      expect(await service.avisosDiarios(d('2026-10-01'))).toEqual({ avisos: 0 });
      expect(alertas.registrar).not.toHaveBeenCalled();
    });
  });
});
