/*
 * Copyright (c) 2016-2020 Flux Capacitor.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.fluxcapacitor.javaclient.tracking;

import io.fluxcapacitor.javaclient.common.exception.FunctionalException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.time.Duration;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;

import static io.fluxcapacitor.common.TimingUtils.retryOnFailure;
import static java.lang.String.format;

@Slf4j
@AllArgsConstructor
public class RetryingErrorHandler implements ErrorHandler {
    private final int retries;
    private final Duration delay;
    private final Predicate<Exception> errorFilter;
    private final boolean throwOnFailure;
    private final boolean logFunctionalErrors;

    public static RetryingErrorHandler forAnyError() {
        return new RetryingErrorHandler(e -> true);
    }

    public RetryingErrorHandler(Predicate<Exception> errorFilter) {
        this(5, Duration.ofSeconds(2), errorFilter, false, true);
    }

    @Override
    public void handleError(Exception error, String errorMessage, Runnable retryFunction) throws Exception {
        if (!errorFilter.test(error)) {
            logError(format("%s. Not retrying, %s", errorMessage, throwOnFailure ? "propagating error" : "continuing."),
                     error);
            if (throwOnFailure) {
                throw error;
            }
            return;
        }
        logError(format("%s. Retrying up to %s times.", errorMessage, retries), error);
        AtomicInteger remainingRetries = new AtomicInteger(retries);
        boolean success = retryOnFailure(retryFunction, delay,
                                         e -> errorFilter.test(e) && remainingRetries.decrementAndGet() > 0);
        if (success) {
            log.info("Message handling was successful on retry");
        } else {
            if (throwOnFailure) {
                logMessage("Propagating error", isTechnicalError(error));
                throw error;
            } else {
                logMessage(format("%s. Not retrying any further. Continuing with next handler.", errorMessage),
                           isTechnicalError(error));
            }
        }
    }

    protected void logError(String errorMessage, Exception error) {
        if (isTechnicalError(error)) {
            log.error("{}. Continuing...", errorMessage, error);
        } else if (logFunctionalErrors) {
            log.warn("{}. Continuing...", errorMessage, error);
        }
    }

    protected void logMessage(String errorMessage, boolean severe) {
        if (severe) {
            log.error(errorMessage);
        } else if (logFunctionalErrors) {
            log.warn(errorMessage);
        }
    }

    protected boolean isTechnicalError(Exception error) {
        return !(error instanceof FunctionalException);
    }
}

@@ -1763,17 +1763,17 @@
 retrying
-,
+.
  %25s%22, er
@@ -1802,13 +1802,33 @@
 lure
+%0A                   
  ? %22
-p
+P
 ropa
@@ -1843,14 +1843,15 @@
 rror
+.
 %22 : %22
-c
+C
 onti
@@ -1859,33 +1859,30 @@
 uing
-.%22),%0A                    
+ with next handler.%22),
  err
@@ -1985,32 +1985,33 @@
 turn;%0A        %7D%0A
+%0A
         logError
@@ -2005,32 +2005,25 @@
      log
-Error(format(%22%25s
+.warn(%22%7B%7D
 . Retryi
@@ -2031,18 +2031,18 @@
 g up to 
-%25s
+%7B%7D
  times.%22
@@ -2064,17 +2064,16 @@
  retries
-)
 , error)
@@ -2479,17 +2479,46 @@
  log
-Message(%22
+.error(%22%7B%7D. Not retrying any further. 
 Prop
@@ -2534,35 +2534,32 @@
 rror
+.
 %22, 
-isTechnicalError(
+errorMessage, 
 error)
-)
 ;%0A  
@@ -2629,26 +2629,30 @@
  log
-Message(format(%22%25s
+Error(errorMessage + %22
 . No
@@ -2716,68 +2716,8 @@
 rror
-Message),%0A                           isTechnicalError(error)
 );%0A 
@@ -2744,39 +2744,37 @@
  %7D%0A    %7D%0A%0A    pr
-otec
+iva
 te
-d
  void logError(S
@@ -2771,38 +2771,33 @@
 logError(String 
-errorM
+m
 essage, Exceptio
@@ -2872,35 +2872,9 @@
 ror(
-%22%7B%7D. Continuing...%22, errorM
+m
 essa
@@ -2952,35 +2952,9 @@
 arn(
-%22%7B%7D. Continuing...%22, errorM
+m
 essa
@@ -3004,23 +3004,23 @@
 void log
-Message
+Warning
 (String 
@@ -3037,22 +3037,23 @@
 ge, 
-boolean severe
+Exception error
 ) %7B%0A
@@ -3068,14 +3068,31 @@
 if (
-severe
+isTechnicalError(error)
 ) %7B%0A
@@ -3109,24 +3109,30 @@
   log.error(
+%22%7B%7D%22, 
 errorMessage
@@ -3123,32 +3123,39 @@
 %7D%22, errorMessage
+, error
 );%0A        %7D els
@@ -3204,16 +3204,22 @@
 og.warn(
+%22%7B%7D%22, 
 errorMes
@@ -3222,16 +3222,23 @@
 rMessage
+, error
 );%0A     

Improve logging levels in RetryingErrorHandler