package com.eactive.eai.apim.apigroup;

import com.eactive.eai.common.dao.DAOException;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.lifecycle.Lifecycle;
import com.eactive.eai.common.lifecycle.LifecycleException;
import com.eactive.eai.common.lifecycle.LifecycleListener;
import com.eactive.eai.common.lifecycle.LifecycleSupport;
import com.eactive.eai.common.util.ApplicationContextProvider;
import com.eactive.eai.common.util.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

@Component
public class ApiGroupManager implements Lifecycle {
    static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
    private boolean started;

    @Autowired
    private ApiGroupDao dao;

    private Map<String, ApiGroupVO> apiGroupMap = new HashMap<>();
    private Map<String, Set<String>> apiGroupApiIndex = new HashMap<>();

    private LifecycleSupport lifecycle = new LifecycleSupport(this);

    public static ApiGroupManager getInstance() {
        return ApplicationContextProvider.getContext().getBean(ApiGroupManager.class);
    }

    @Override
    public void addLifecycleListener(LifecycleListener listener) {
        this.lifecycle.addLifecycleListener(listener);
    }

    @Override
    public LifecycleListener[] findLifecycleListeners() {
        return this.lifecycle.findLifecycleListeners();
    }

    @Override
    public void removeLifecycleListener(LifecycleListener listener) {
        this.lifecycle.removeLifecycleListener(listener);
    }

    public void reloadApiGroup(String apiGroupId) throws DAOException {
        removeApiGroup(apiGroupId);
        ApiGroupVO apiGroupVO = dao.getApiGroup(apiGroupId);
        addApiGroup(apiGroupVO);
    }

    protected void addApiGroup(ApiGroupVO apiGroupVO){
        apiGroupMap.put(apiGroupVO.getId(), apiGroupVO);
        List<ApiGroupApiVO> apiList = apiGroupVO.getApiGroupApiList();
        if(apiList != null){
            for (ApiGroupApiVO apiGroupApiVO : apiList){
                String apiId = apiGroupApiVO.getApiId();
                String apiGroupId = apiGroupApiVO.getApiGroupId();
                apiGroupApiIndex.computeIfAbsent(apiId, k -> new HashSet<>()).add(apiGroupId);
            }
        }
    }

    public void removeApiGroup(String apiGroupId) {
        // apiGroupMap에서 ApiGroupVO 제거
        ApiGroupVO removedApiGroup = apiGroupMap.remove(apiGroupId);

        // 제거된 ApiGroupVO가 null이 아닌지 확인
        if (removedApiGroup != null) {
            // 제거된 ApiGroupVO의 ApiGroupApiVO 리스트 가져오기
            List<ApiGroupApiVO> apiList = removedApiGroup.getApiGroupApiList();

            // apiList가 null이 아닌 경우
            if (apiList != null) {
                for (ApiGroupApiVO apiGroupApiVO : apiList) {
                    String apiId = apiGroupApiVO.getApiId();

                    // apiGroupApiIndex에서 apiId에 해당하는 Set 가져오기
                    Set<String> apiGroupIds = apiGroupApiIndex.get(apiId);

                    // Set이 null이 아닌 경우 apiGroupId 제거
                    if (apiGroupIds != null) {
                        apiGroupIds.remove(apiGroupId);

                        // Set이 비어 있으면 apiGroupApiIndex에서 해당 apiId 삭제
                        if (apiGroupIds.isEmpty()) {
                            apiGroupApiIndex.remove(apiId);
                        }
                    }
                }
            }
        }
    }


    public List<ApiGroupVO> findApiGroup(String apiId) {
        return Optional.ofNullable(apiGroupApiIndex.get(apiId)).orElse(Collections.emptySet())
                .stream()
                .map(apiGroupMap::get)
                .collect(Collectors.toList());
    }

    private void init() throws Exception {
        try {
            List<ApiGroupVO> apiGroups = dao.getAllApiGroups();
            for(ApiGroupVO apiGroupVO : apiGroups){
                addApiGroup(apiGroupVO);
            }
        } catch (DAOException e) {
            logger.error(e);
            throw new LifecycleException(ExceptionUtil.getErrorCode(e, "RECEAICPM201"));
        }
    }

    @Override
    public void start() throws LifecycleException {
        if (started)
            throw new LifecycleException("RECEAICCM201");

        this.lifecycle.fireLifecycleEvent(STARTING_EVENT, this);

        try {
            init();
        } catch (Exception e) {
            throw new LifecycleException(ExceptionUtil.getErrorCode(e, "RECEAICCM202"));
        }

        this.started = true;
        this.lifecycle.fireLifecycleEvent(STARTED_EVENT, this);
    }

    @Override
    public void stop() throws LifecycleException {
        if (!started)
            throw new LifecycleException("RECEAIAAC020");

        // Notify our interested LifecycleListeners
        this.lifecycle.fireLifecycleEvent(STOPING_EVENT, this);

        this.apiGroupMap = new HashMap<>();

        this.started = false;
        // Notify our interested LifecycleListeners
        this.lifecycle.fireLifecycleEvent(STOPPED_EVENT, this);
    }

    @Override
    public boolean isStarted() {
        return this.started;
    }
}
