import { BadRequestException } from '@nestjs/common';
import { TeamRolesEnum } from '../team-roles/team-roles.enum';
import { RecurringTransactionIntervalEnum } from '../recurring-transactions/recurring-transaction-interval.enum';
import { CashboxExportSubscriptionService } from './cashbox-export-subscription.service';

describe('CashboxExportSubscriptionService', () => {
  const repository = { findOne: jest.fn(), create: jest.fn((v) => v), save: jest.fn(async (v) => v) };
  const access = { assertAtLeast: jest.fn() };
  let service: CashboxExportSubscriptionService;

  beforeEach(() => {
    jest.clearAllMocks();
    service = new CashboxExportSubscriptionService(repository as any, access as any);
  });

  it('returns a paused default when no subscription exists yet', async () => {
    repository.findOne.mockResolvedValue(null);

    await expect(service.getSubscription(5, 42)).resolves.toEqual({
      recipients: [],
      interval: RecurringTransactionIntervalEnum.monthly,
      active: false,
      nextRunDate: null,
    });
    expect(access.assertAtLeast).toHaveBeenCalledWith(
      42,
      5,
      'transaction_create_min_role',
      TeamRolesEnum.scnd_treasurer,
    );
  });

  it('returns the existing subscription', async () => {
    repository.findOne.mockResolvedValue({
      recipients: ['vorstand@example.com'],
      interval: RecurringTransactionIntervalEnum.yearly,
      active: true,
      nextRunDate: '2027-01-01T00:00:00.000Z',
    });

    await expect(service.getSubscription(5, 42)).resolves.toEqual({
      recipients: ['vorstand@example.com'],
      interval: RecurringTransactionIntervalEnum.yearly,
      active: true,
      nextRunDate: '2027-01-01T00:00:00.000Z',
    });
  });

  it('rejects activating with an empty recipient list', async () => {
    repository.findOne.mockResolvedValue(null);

    await expect(
      service.upsertSubscription(5, 42, {
        recipients: [],
        interval: RecurringTransactionIntervalEnum.monthly,
        active: true,
      }),
    ).rejects.toBeInstanceOf(BadRequestException);
    expect(repository.save).not.toHaveBeenCalled();
  });

  it('creates a new subscription and computes the next period boundary on first activation', async () => {
    repository.findOne.mockResolvedValue(null);
    jest.useFakeTimers().setSystemTime(new Date('2026-08-15T10:00:00.000Z'));

    const result = await service.upsertSubscription(5, 42, {
      recipients: ['vorstand@example.com'],
      interval: RecurringTransactionIntervalEnum.monthly,
      active: true,
    });

    expect(result.nextRunDate).toBe('2026-09-01T00:00:00.000Z');
    expect(repository.save).toHaveBeenCalledWith(
      expect.objectContaining({
        recipients: ['vorstand@example.com'],
        interval: RecurringTransactionIntervalEnum.monthly,
        active: true,
        nextRunDate: '2026-09-01T00:00:00.000Z',
      }),
    );
    jest.useRealTimers();
  });

  it('keeps the existing nextRunDate when editing recipients without changing interval or activation state', async () => {
    repository.findOne.mockResolvedValue({
      recipients: ['old@example.com'],
      interval: RecurringTransactionIntervalEnum.monthly,
      active: true,
      nextRunDate: '2026-09-01T00:00:00.000Z',
    });

    const result = await service.upsertSubscription(5, 42, {
      recipients: ['new@example.com'],
      interval: RecurringTransactionIntervalEnum.monthly,
      active: true,
    });

    expect(result.nextRunDate).toBe('2026-09-01T00:00:00.000Z');
  });

  it('recomputes nextRunDate when the interval changes', async () => {
    repository.findOne.mockResolvedValue({
      recipients: ['a@example.com'],
      interval: RecurringTransactionIntervalEnum.monthly,
      active: true,
      nextRunDate: '2026-09-01T00:00:00.000Z',
    });
    jest.useFakeTimers().setSystemTime(new Date('2026-08-15T10:00:00.000Z'));

    const result = await service.upsertSubscription(5, 42, {
      recipients: ['a@example.com'],
      interval: RecurringTransactionIntervalEnum.yearly,
      active: true,
    });

    expect(result.nextRunDate).toBe('2027-08-01T00:00:00.000Z');
    jest.useRealTimers();
  });
});
