package hr.com.port.ips.eracun.resilience.mer;

import java.util.concurrent.ConcurrentHashMap;
import org.apache.log4j.Logger;

public final class MerAvailabilityGate {
	

    public static final class State {
        public int consecutiveFails;
        public long openUntilEpochMs; // 0 = CLOSED
        public boolean halfOpenProbeInFlight;
    }

    private static final long WARN_COOLDOWN_MS = 60_000L; // 1 min (podesivo)
    private static final ConcurrentHashMap<String, Long> lastWarnAt = new ConcurrentHashMap<>();
	private static final ConcurrentHashMap<String, State> STATES = new ConcurrentHashMap<String, State>();
	private static final Logger logger = Logger.getLogger(MerAvailabilityGate.class);	

    private static volatile int FAILURE_THRESHOLD = 5;
    private static volatile long COOLDOWN_MS = 120000L; // 120 s
	
	// --- NEW: singleton ---
    private static final MerAvailabilityGate INSTANCE = new MerAvailabilityGate();
    public static MerAvailabilityGate getInstance() { return INSTANCE; }

    private MerAvailabilityGate() {}

    public static void configure(int failureThreshold, long cooldownMs) {
        FAILURE_THRESHOLD = failureThreshold;
        COOLDOWN_MS = cooldownMs;
    }

    public static boolean allow(String key) {
        State s = STATES.get(key);
        if (s == null) return true;
        long now = System.currentTimeMillis();
        if (s.openUntilEpochMs == 0L) return true; // CLOSED
        if (now >= s.openUntilEpochMs) {
            // HALF_OPEN – dopusti jedan probni poziv
            if (!s.halfOpenProbeInFlight) {
                s.halfOpenProbeInFlight = true;
                return true;
            }
            return false;
        }
        return false; // OPEN
    }

    public static void onSuccess(String key) {
        State s = STATES.get(key);
        if (s == null) return;
        s.consecutiveFails = 0;
        s.openUntilEpochMs = 0L;
        s.halfOpenProbeInFlight = false;
		if (logger.isDebugEnabled()) {
            logger.debug("eRacun provider recovered. OPKEY=" + key);
        }
    }

    public static void onFailure(String key) {
        State s = STATES.get(key);
        if (s == null) {
            s = new State();
            STATES.put(key, s);
        }
        s.consecutiveFails++;
        if (s.consecutiveFails >= FAILURE_THRESHOLD) {
            s.openUntilEpochMs = System.currentTimeMillis() + COOLDOWN_MS;
            s.halfOpenProbeInFlight = false;
        }
		maybeWarn(key);
    }
	
	private static void maybeWarn(String opKey) {
        long now = System.currentTimeMillis();
        Long prev = lastWarnAt.get(opKey);
        if (prev == null || (now - prev) >= WARN_COOLDOWN_MS) {
            // provider-agnostički, kratak i dosljedan message
            logger.warn("soft-fail OPKEY=" + opKey);
            lastWarnAt.put(opKey, now);
        }
    }
	
	// --- NEW: enum sugar (iz Koraka 2) ---
    public boolean isSoftFail(MerApiCallType type) {
        return type != null && MerSoftFailContext.wasSoftFailFor(type);
    }

    public boolean guard(MerApiCallType type, String logPrefix) {
        if (type != null && MerSoftFailContext.wasSoftFailFor(type)) {
            if (logPrefix != null && !logPrefix.isEmpty()) {
                logger.warn(logPrefix + " – preskačem zbog recentnog soft-faila za " + type.name());
            }
            return true; // skip
        }
        return false; // continue
    }
}