import { ContractExpiryService } from './contract-expiry.service';
import type { Contract } from './entities/contract.entity';

/**
 * The reminder must warn each customer once, ten days out, and must never let a
 * notification failure escape into the scheduler (that would also take out the
 * expiry sweep sharing this service).
 */
describe('ContractExpiryService — expiry reminder', () => {
  const contract = (over: Partial<Contract> = {}): Contract =>
    ({
      id: 'c-1',
      reference: 'CTR-2026-00042',
      companyId: 'co-1',
      contractValidUntil: new Date('2026-08-10T00:00:00.000Z'),
      status: 'CONTRACT_ACTIVE',
      ...over,
    }) as Contract;

  let repo: { expireLapsedContracts: jest.Mock; findExpiringInDays: jest.Mock };
  let inbox: { notify: jest.Mock };
  let service: ContractExpiryService;

  beforeEach(() => {
    repo = {
      expireLapsedContracts: jest.fn().mockResolvedValue(0),
      findExpiringInDays: jest.fn().mockResolvedValue([]),
    };
    inbox = { notify: jest.fn().mockResolvedValue(undefined) };
    service = new ContractExpiryService(repo as never, inbox as never);
  });

  it('asks for the contracts lapsing ten days out', async () => {
    await service.remindExpiringContracts();
    expect(repo.findExpiringInDays).toHaveBeenCalledWith(10);
  });

  it('notifies the owning company once, deep-linking the contract list', async () => {
    repo.findExpiringInDays.mockResolvedValue([contract()]);

    await service.remindExpiringContracts();

    expect(inbox.notify).toHaveBeenCalledTimes(1);
    const sent = inbox.notify.mock.calls[0][0];
    expect(sent.recipients).toEqual({ companyId: 'co-1' });
    expect(sent.title).toContain('CTR-2026-00042');
    expect(sent.title).toContain('10 days');
    expect(sent.link).toBe('/contracts');
    expect(sent.data).toMatchObject({ contractId: 'c-1', action: 'CONTRACT_EXPIRING' });
  });

  it('skips a contract with no owning company (nobody to notify)', async () => {
    repo.findExpiringInDays.mockResolvedValue([contract({ companyId: null })]);
    await service.remindExpiringContracts();
    expect(inbox.notify).not.toHaveBeenCalled();
  });

  it('swallows a notification failure instead of throwing into the scheduler', async () => {
    repo.findExpiringInDays.mockResolvedValue([contract()]);
    inbox.notify.mockRejectedValue(new Error('inbox down'));
    await expect(service.remindExpiringContracts()).resolves.toBeUndefined();
  });
});
