package org.apereo.cas.impl.calcs;

import org.apereo.cas.authentication.Authentication;
import org.apereo.cas.services.RegisteredService;
import org.apereo.cas.support.events.dao.CasEvent;
import org.apereo.cas.support.events.CasEventRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.ZonedDateTime;
import java.util.Collection;

/**
 * This is {@link DateTimeAuthenticationRequestRiskCalculator}.
 *
 * @author Misagh Moayyed
 * @since 5.1.0
 */
public class DateTimeAuthenticationRequestRiskCalculator extends BaseAuthenticationRequestRiskCalculator {
    private static final Logger LOGGER = LoggerFactory.getLogger(DateTimeAuthenticationRequestRiskCalculator.class);
    
    private final int windowInHours;

    public DateTimeAuthenticationRequestRiskCalculator(final CasEventRepository casEventRepository, final int windowInHours) {
        super(casEventRepository);
        this.windowInHours = windowInHours;
    }

    @Override
    protected BigDecimal calculateScore(final HttpServletRequest request, final Authentication authentication,
                                        final RegisteredService service, final Collection<CasEvent> events) {
        final ZonedDateTime timestamp = ZonedDateTime.now();
        LOGGER.debug("Filtering authentication events for timestamp [{}]", timestamp);

        final long count = events.stream().filter(e -> {
            final int hour = timestamp.getHour();
            return e.getCreationTime().getHour() == hour
                    || e.getCreationTime().plusHours(windowInHours).getHour() == hour
                    || e.getCreationTime().minusHours(windowInHours).getHour() == hour;
        }).count();
        
        LOGGER.debug("Total authentication events found for [{}]: [{}]", timestamp, count);
        if (count == events.size()) {
            LOGGER.debug("Principal [{}] has always authenticated from [{}]", authentication.getPrincipal(), timestamp);
            return LOWEST_RISK_SCORE;
        }
        return getFinalAveragedScore(count, events.size());
    }
}

@@ -405,16 +405,45 @@
 teTime;%0A
+import java.time.ZoneOffset;%0A
 import j
@@ -805,20 +805,16 @@
 class);%0A
-    
 %0A    pri
@@ -1350,16 +1350,30 @@
 ime.now(
+ZoneOffset.UTC
 );%0A     
@@ -1454,25 +1454,24 @@
 stamp);%0A
-%0A
         
 final lo
@@ -1462,22 +1462,16 @@
         
-final 
 long cou
@@ -1479,76 +1479,169 @@
 t = 
-events.stream().filter(e -%3E %7B%0A            final int hour = timestamp
+0;%0A%0A        if (timestamp.getHour() %3C= timestamp.plusHours(windowInHours).getHour()%0A                && timestamp.getHour() %3E= timestamp.minusHours(windowInHours)
 .get
@@ -1646,17 +1646,18 @@
 etHour()
-;
+)%7B
 %0A       
@@ -1661,23 +1661,50 @@
         
-return 
+count = events.stream().filter(e-%3E
 e.getCre
@@ -1717,39 +1717,79 @@
 ime().getHour() 
-== h
+%3C= timestamp.plusHours(windowInHours).getH
 our
+()
 %0A               
@@ -1781,38 +1781,34 @@
                 
-    %7C%7C
+&&
  e.getCreationTi
@@ -1812,18 +1812,42 @@
 nTime().
-pl
+getHour() %3E= timestamp.min
 usHours(
@@ -1874,114 +1874,259 @@
 ur()
- == hour%0A                    %7C%7C e.getCreationTime().minusHours(windowInHours).getHour() == hour;%0A        %7D
+).count();%0A        %7D else %7B%0A            count = events.stream().filter(e -%3E e.getCreationTime().getHour() %3C= timestamp.plusHours(windowInHours).getHour()%0A                %7C%7C e.getCreationTime().getHour() %3E= timestamp.minusHours(windowInHours).getHour()
 ).co
@@ -2140,16 +2140,18 @@
         
+%7D%0A
 %0A       
@@ -2207,16 +2207,34 @@
 for %5B%7B%7D%5D
+ in a %5B%7B%7D%5Dh window
 : %5B%7B%7D%5D%22,
@@ -2244,16 +2244,31 @@
 mestamp,
+ windowInHours,
  count);

Fix windowInHours checking. (#3077)