public static Instant getNextRecommendedNotificationTime(final Account account,
      final LocalTime preferredTime,
      final Clock clock) {

    final ZonedDateTime candidateNotificationTime = getZoneOffset(account, clock)
        .map(zoneOffset -> ZonedDateTime.now(zoneOffset).with(preferredTime))
        .orElseGet(() -> {
          // We couldn't find a reasonable timezone for the account for some reason, so make an educated guess at a
          // reasonable time to send a notification based on the account's creation time.
          final Instant accountCreation = Instant.ofEpochMilli(account.getPrimaryDevice().getCreated());
          final LocalTime accountCreationLocalTime = LocalTime.ofInstant(accountCreation, ZoneId.systemDefault());

          return ZonedDateTime.now(ZoneId.systemDefault()).with(accountCreationLocalTime);
        });

    if (candidateNotificationTime.toInstant().isBefore(clock.instant())) {
      // We've missed our opportunity today, so go for the same time tomorrow
      return candidateNotificationTime.plusDays(1).toInstant();
    } else {
      // The best time to send a notification hasn't happened yet today
      return candidateNotificationTime.toInstant();
    }
  }
@Test
  void getNextRecommendedNotificationTime() {
    {
      final Account account = mock(Account.class);

      // The account has a phone number that can be resolved to a region with known timezones
      when(account.getNumber()).thenReturn(PhoneNumberUtil.getInstance().format(
          PhoneNumberUtil.getInstance().getExampleNumber("DE"), PhoneNumberUtil.PhoneNumberFormat.E164));

      final ZoneId berlinZoneId = ZoneId.of("Europe/Berlin");
      final ZonedDateTime beforeNotificationTime = ZonedDateTime.now(berlinZoneId).with(LocalTime.of(13, 0));

      assertEquals(
          beforeNotificationTime.with(LocalTime.of(14, 0)).toInstant(),
          SchedulingUtil.getNextRecommendedNotificationTime(account, LocalTime.of(14, 0),
              Clock.fixed(beforeNotificationTime.toInstant(), berlinZoneId)));

      final ZonedDateTime afterNotificationTime = ZonedDateTime.now(berlinZoneId).with(LocalTime.of(15, 0));

      assertEquals(
          afterNotificationTime.with(LocalTime.of(14, 0)).plusDays(1).toInstant(),
          SchedulingUtil.getNextRecommendedNotificationTime(account, LocalTime.of(14, 0),
              Clock.fixed(afterNotificationTime.toInstant(), berlinZoneId)));
    }

    {
      final Account account = mock(Account.class);
      final Device primaryDevice = mock(Device.class);

      // The account does not have a phone number that can be connected to a region/time zone
      when(account.getNumber()).thenReturn("Not a parseable number");
      when(account.getPrimaryDevice()).thenReturn(primaryDevice);
      when(primaryDevice.getCreated())
          .thenReturn(ZonedDateTime.of(2024, 7, 10, 9, 53, 12, 0, ZoneId.systemDefault()).toInstant().toEpochMilli());

      final ZonedDateTime beforeNotificationTime = ZonedDateTime.now(ZoneId.systemDefault()).with(LocalTime.of(9, 0));

      assertEquals(
          beforeNotificationTime.with(LocalTime.of(9, 53, 12)).toInstant(),
          SchedulingUtil.getNextRecommendedNotificationTime(account, LocalTime.of(14, 0),
              Clock.fixed(beforeNotificationTime.toInstant(), ZoneId.systemDefault())));

      final ZonedDateTime afterNotificationTime = ZonedDateTime.now(ZoneId.systemDefault()).with(LocalTime.of(10, 0));

      assertEquals(
          afterNotificationTime.with(LocalTime.of(9, 53, 12)).plusDays(1).toInstant(),
          SchedulingUtil.getNextRecommendedNotificationTime(account, LocalTime.of(14, 0),
              Clock.fixed(afterNotificationTime.toInstant(), ZoneId.systemDefault())));
    }
  }