package com.eactive.eai.custom.inflow;

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.stereotype.Component;

import com.eactive.eai.common.inflow.Bucket;
import com.eactive.eai.common.inflow.InflowTargetVO;
import com.eactive.eai.common.inflow.InflowType;
import com.eactive.eai.common.inflow.dual.DualCustomBucket;
import com.eactive.eai.common.inflow.dual.DualInflowControlManager;
import com.eactive.eai.common.lifecycle.LifecycleException;
import com.eactive.eai.common.util.ApplicationContextProvider;
import com.eactive.eai.common.util.Logger;

/**
 * 어댑터/인터페이스 이중 버킷(DualInflowControlManager)에 클라이언트 유량제어를 추가한 관리자.
 *
 * <p>적용 방법: INFLOW.properties에 아래 한 줄 추가.
 * <pre>inflow.control.bucket.className=com.eactive.eai.common.inflow.dual.ClientDualInflowControlManager</pre>
 */
@Component
public class ClientDualInflowControlManager extends DualInflowControlManager implements ClientDualBucket {

    private static final Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);

    public static synchronized Bucket getBucket() {
        return ApplicationContextProvider.getContext().getBean(ClientDualInflowControlManager.class);
    }

    private volatile Map<String, DualCustomBucket> clientBucketMap = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, TargetMetrics> clientMetricsMap = new ConcurrentHashMap<>();

    // -------------------------------------------------------------------------
    // Lifecycle
    // -------------------------------------------------------------------------

    @Override
    public void start() throws LifecycleException {
        super.start();
        try {
            initClients();
        } catch (Exception e) {
            throw new LifecycleException("ClientDualInflowControlManager init failed: " + e.getMessage());
        }
    }

    // -------------------------------------------------------------------------
    // 초기화 / 리로드
    // -------------------------------------------------------------------------

    private void initClients() throws Exception {
        this.clientBucketMap = loadBucketMap(InflowType.CLIENT);
        if (logger.isInfo()) logger.info("ClientDualInflowControlManager] initClients: " + clientBucketMap.size() + " buckets");
    }

    public synchronized void reloadClient() throws Exception {
        initClients();
        clientMetricsMap.clear();
    }

    public synchronized void reloadClient(String clientId) throws Exception {
        reloadBucketMap(clientBucketMap, InflowType.CLIENT, clientId);
        TargetMetrics m = clientMetricsMap.get(clientId);
        if (m != null) m.reset();
    }

    public synchronized void removeClient(String clientId) {
        clientBucketMap.remove(clientId);
        clientMetricsMap.remove(clientId);
    }

    // -------------------------------------------------------------------------
    // 클라이언트 메트릭 접근자
    // -------------------------------------------------------------------------

    public TargetMetrics getClientMetrics(String clientId)    { return clientMetricsMap.get(clientId); }
    public Map<String, TargetMetrics> getAllClientMetrics()    { return Collections.unmodifiableMap(clientMetricsMap); }
    public void resetClientMetrics(String clientId)           { TargetMetrics m = clientMetricsMap.get(clientId); if (m != null) m.reset(); }
    public void resetAllClientMetrics()                       { clientMetricsMap.values().forEach(TargetMetrics::reset); }

    // -------------------------------------------------------------------------
    // ClientDualBucket — 클라이언트 유량제어
    // -------------------------------------------------------------------------

    @Override
    public String isClientPassDetail(String clientId) {
        DualCustomBucket b = clientBucketMap.get(clientId);
        if (b == null) return DualCustomBucket.RESULT_PASS;
        String result = b.tryConsume();
        recordMetrics(clientMetricsMap, clientId, result);
        return result;
    }

    @Override
    public InflowTargetVO getClientInflowThreshold(String clientId) {
        DualCustomBucket b = clientBucketMap.get(clientId);
        return (b == null) ? null : b.getInflowTargetVo();
    }

    // -------------------------------------------------------------------------
    // 모니터링용 접근자
    // -------------------------------------------------------------------------

    public DualCustomBucket getClientBucket(String clientId) {
        return clientBucketMap.get(clientId);
    }

    public Map<String, DualCustomBucket> getClientBucketMap() {
        return clientBucketMap;
    }
}
