package com.eactive.eai.common.inflow.dual;

import com.eactive.eai.common.inflow.InflowTargetVO;
import io.github.bucket4j.local.LocalBucket;

/**
 * 어댑터/인터페이스 유량제어 버킷.
 * 기존 CustomBucket(단일 버킷)과 달리 perSecond/threshold를 별도 버킷으로 분리하여
 * 어느 한도를 초과했는지 구분 가능 (CustomGroupBucket과 동일한 이중 구조).
 */
public class DualCustomBucket {

    public static final String RESULT_PASS               = null;
    public static final String RESULT_BLOCKED_PER_SECOND = "PER_SECOND";
    public static final String RESULT_BLOCKED_THRESHOLD  = "THRESHOLD";

    private final LocalBucket    perSecondBucket;
    private final LocalBucket    thresholdBucket;
    private final InflowTargetVO inflowTargetVo;

    public DualCustomBucket(LocalBucket perSecondBucket, LocalBucket thresholdBucket, InflowTargetVO inflowTargetVo) {
        this.perSecondBucket = perSecondBucket;
        this.thresholdBucket = thresholdBucket;
        this.inflowTargetVo  = inflowTargetVo;
    }

    /**
     * 두 버킷을 순서대로 소비 시도.
     * @return null이면 통과, "PER_SECOND" 또는 "THRESHOLD"이면 해당 버킷에서 차단
     */
    public String tryConsume() {
        boolean perSecondPass = (perSecondBucket == null) || perSecondBucket.tryConsume(1);
        if (!perSecondPass) {
            return RESULT_BLOCKED_PER_SECOND;
        }

        boolean thresholdPass = (thresholdBucket == null) || thresholdBucket.tryConsume(1);
        if (!thresholdPass) {
            // perSecond 토큰은 이미 소비됨 — Token Bucket 특성상 롤백 불가
            // 초당 버킷은 빠르게 리필되므로 실질적 영향 미미
            return RESULT_BLOCKED_THRESHOLD;
        }

        return RESULT_PASS;
    }

    public long getPerSecondAvailableTokens() {
        return (perSecondBucket != null) ? perSecondBucket.getAvailableTokens() : -1;
    }

    public long getThresholdAvailableTokens() {
        return (thresholdBucket != null) ? thresholdBucket.getAvailableTokens() : -1;
    }

    public LocalBucket    getPerSecondBucket() { return perSecondBucket; }
    public LocalBucket    getThresholdBucket() { return thresholdBucket; }
    public InflowTargetVO getInflowTargetVo()  { return inflowTargetVo; }
}
