import { CrmSeedService } from './crm-seed.service';
import { ETAPAS_DEFAULT, MOTIVOS_DEFAULT, SLA_DEFAULT } from './crm-config-default.seed';

describe('datos por defecto vs spec', () => {
  it('son las 6 etapas de §5.3, con sus probabilidades', () => {
    expect(ETAPAS_DEFAULT.map((e) => [e.nombre, e.probabilidad])).toEqual([
      ['Calificación', 10],
      ['Necesidad / Demo', 30],
      ['Propuesta enviada', 50],
      ['Negociación', 70],
      ['Ganada', 100],
      ['Perdida', 0],
    ]);
  });

  it('hay exactamente una etapa ganada y una perdida', () => {
    expect(ETAPAS_DEFAULT.filter((e) => e.es_ganada)).toHaveLength(1);
    expect(ETAPAS_DEFAULT.filter((e) => e.es_perdida)).toHaveLength(1);
    // Ninguna es las dos cosas: el servicio lo rechazaría al crearla a mano.
    expect(ETAPAS_DEFAULT.filter((e) => e.es_ganada && e.es_perdida)).toHaveLength(0);
  });

  it('el orden no tiene huecos ni repetidos', () => {
    expect(ETAPAS_DEFAULT.map((e) => e.orden)).toEqual([0, 1, 2, 3, 4, 5]);
  });

  it('los SLA de §6.1 en minutos: 1h/8h, 4h/24h, 8h/72h', () => {
    expect(SLA_DEFAULT.map((s) => [s.prioridad, s.min_primera_respuesta, s.min_resolucion])).toEqual([
      ['alta', 60, 480],
      ['media', 240, 1440],
      ['baja', 480, 4320],
    ]);
  });

  it('los motivos cubren los tres tipos que acepta el CHECK de la tabla', () => {
    expect(new Set(MOTIVOS_DEFAULT.map((m) => m.tipo))).toEqual(new Set(['perdida', 'descarte', 'churn']));
  });
});

describe('CrmSeedService', () => {
  const crear = (overrides: Record<string, any> = {}) => {
    const tx: any = {
      crm_calendarios: {
        findFirst: jest.fn().mockResolvedValue(null),
        create: jest.fn().mockResolvedValue({ id: 'cal-1' }),
      },
      crm_pipelines: {
        findFirst: jest.fn().mockResolvedValue(null),
        create: jest.fn().mockResolvedValue({ id: 'pip-1' }),
      },
      crm_etapas: { createMany: jest.fn().mockResolvedValue({ count: 6 }) },
      crm_origenes: { createMany: jest.fn().mockResolvedValue({ count: 11 }) },
      crm_motivos: { createMany: jest.fn().mockResolvedValue({ count: 13 }) },
      crm_sla_politicas: { createMany: jest.fn().mockResolvedValue({ count: 3 }) },
      crm_parametros: { createMany: jest.fn().mockResolvedValue({ count: 1 }) },
      ...overrides,
    };
    const prisma: any = {
      ...tx,
      $transaction: jest.fn().mockImplementation((cb: any) => cb(tx)),
    };
    return { service: new CrmSeedService(prisma as any), prisma, tx };
  };

  it('en una empresa en blanco crea todo', async () => {
    const { service } = crear();

    const r = await service.asegurarConfiguracion('emp-1', 'user-1');

    expect(r).toEqual({ pipeline: 1, etapas: 6, origenes: 11, motivos: 13, calendario: 1, sla: 3, parametros: 1 });
  });

  it('todo se inserta con skipDuplicates: correrlo dos veces no duplica ni falla', async () => {
    const { service, tx } = crear();

    await service.asegurarConfiguracion('emp-1');

    for (const tabla of ['crm_etapas', 'crm_origenes', 'crm_motivos', 'crm_sla_politicas', 'crm_parametros']) {
      expect(tx[tabla].createMany.mock.calls[0][0].skipDuplicates).toBe(true);
    }
  });

  it('no recrea el pipeline ni el calendario si ya existen', async () => {
    const { service, tx } = crear({
      crm_calendarios: { findFirst: jest.fn().mockResolvedValue({ id: 'cal-vieja' }), create: jest.fn() },
      crm_pipelines: { findFirst: jest.fn().mockResolvedValue({ id: 'pip-viejo' }), create: jest.fn() },
    });

    const r = await service.asegurarConfiguracion('emp-1');

    expect(tx.crm_calendarios.create).not.toHaveBeenCalled();
    expect(tx.crm_pipelines.create).not.toHaveBeenCalled();
    expect(r.pipeline).toBe(0);
    expect(r.calendario).toBe(0);
  });

  it('las etapas cuelgan del pipeline y llevan el empresa_id', async () => {
    const { service, tx } = crear();

    await service.asegurarConfiguracion('emp-1');

    const data = tx.crm_etapas.createMany.mock.calls[0][0].data;
    expect(data).toHaveLength(6);
    expect(data.every((e: any) => e.pipeline_id === 'pip-1' && e.empresa_id === 'emp-1')).toBe(true);
  });

  it('las políticas de SLA apuntan al calendario comercial', async () => {
    const { service, tx } = crear();

    await service.asegurarConfiguracion('emp-1');

    const data = tx.crm_sla_politicas.createMany.mock.calls[0][0].data;
    expect(data.every((s: any) => s.calendario_id === 'cal-1')).toBe(true);
  });

  it('todo corre en una sola transacción', async () => {
    const { service, prisma } = crear();

    await service.asegurarConfiguracion('emp-1');

    expect(prisma.$transaction).toHaveBeenCalledTimes(1);
  });

  describe('necesitaSeed', () => {
    it('es true cuando la empresa no tiene ningún pipeline', async () => {
      const { service } = crear({ crm_pipelines: { findFirst: jest.fn().mockResolvedValue(null) } });
      expect(await service.necesitaSeed('emp-1')).toBe(true);
    });

    it('es false cuando ya tiene uno', async () => {
      const { service } = crear({ crm_pipelines: { findFirst: jest.fn().mockResolvedValue({ id: 'p' }) } });
      expect(await service.necesitaSeed('emp-1')).toBe(false);
    });
  });
});
