package com.tsi.comm.server.mybatis;

import com.tsi.app.common.utils.TimeUtils;
import com.tsi.comm.server.mybatis.vo.*;
import com.tsi.comm.server.repository.ApplicationRepository;
import com.tsi.comm.server.repository.TsiAlarmManager;
import com.tsi.comm.server.repository.TsiNodeAddManager;
import com.tsi.comm.server.repository.TsiNodeManager;
import com.tsi.comm.server.vo.TsiAlarmConfigVo;
import com.tsi.comm.server.vo.TsiNodeAddDetailVo;
import com.tsi.comm.server.vo.TsiNodeAddVo;
import com.tsi.comm.server.vo.TsiNodeVo;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.List;

@Slf4j
@Getter
@Setter
@RequiredArgsConstructor
@Service
public class TsiDatabaseService {

    private final TsiDatabaseMapper databaseMapper;
    private final TsiNodeManager nodeManager;
    private final TsiAlarmManager alarmManager;
    private final TsiNodeAddManager nodeAddManager;

    public void init() {
        initDatabase();
        loadDatabase();
    }

    public void loadAlarmConfig() {
        long startTime = System.nanoTime();

        List<AlarmConfigVo> objLists = this.databaseMapper.getAlarmConfig();
        for (AlarmConfigVo obj : objLists) {
            TsiAlarmConfigVo alarm = this.alarmManager.get(obj.getAlarmCode());
            if (alarm != null) {
                alarm.setDesc(obj.getDesc());
                alarm.setValue(obj.getValue());
                alarm.setUseYn(obj.getUseYn().equals("Y"));
            }
            else {
                alarm = new TsiAlarmConfigVo();
                alarm.setAlarmCode(obj.getAlarmCode());
                alarm.setDesc(obj.getDesc());
                alarm.setValue(obj.getValue());
                alarm.setUseYn(obj.getUseYn().equals("Y"));
                this.alarmManager.put(obj.getAlarmCode(), alarm);
            }
        }

        log.info("loadAlarmConfig: ..end. {}/{} EA. {}", objLists.size(), this.alarmManager.size(), TimeUtils.elapsedTime(startTime));
    }

    public void initDatabase() {
        int result = this.databaseMapper.insertNodeStatusInitialize();
        //updateNodeStatusTerm();
        log.info("init database: {} EA", result);
    }

    public void loadDatabase() {
        loadTsiNode();
        loadTsiNodeAdd();
        loadAlarmConfig();
    }

    private void loadTsiNode() {
        long startTime = System.nanoTime();

        this.nodeManager.beforeLoading();

        boolean installed;
        TsiNodeVo node;

        int idx = 0;
        List<NodeVo> objLists = this.databaseMapper.getNodeInfoList();
        for (NodeVo obj : objLists) {
            installed = obj.getUseYn().equals("Y");
            node = this.nodeManager.get(obj.getNodeId());
            if (node != null) {
                node.setCheckInstalled(installed);
                node.setSendTest(obj.getTestYn().equals("Y"));
                node.setSendNode(obj.getNodeYn().equals("Y"));
                node.setSendCvim(obj.getCvimYn().equals("Y"));
                node.setRegistered(true);
            }
            else {
                node = new TsiNodeVo(obj.getNodeId(), obj.getIpAddr(), obj.getTestYn().equals("Y"), obj.getNodeYn().equals("Y"), obj.getCvimYn().equals("Y"));
                node.setRegistered(true);
                node.setCheckInstalled(installed);
                this.nodeManager.put(obj.getNodeId(), node);
            }
            node.setIdx(idx++);  // 인덱스 설정
            //int hash = Long.hashCode(obj.getNodeId()); // 해시코드 생성, 필요시 사용
            //int idx = Math.abs(hash % 9);

            final String ipAddr = obj.getIpAddr();
            if (!"0.0.0.0".equals(ipAddr)) {
                this.nodeManager.putIpAddr(ipAddr, node);
            }
        }

        this.nodeManager.afterLoading();

        log.info("loadTsiNode: ..end. {}/{} EA. {}", objLists.size(), this.nodeManager.size(), TimeUtils.elapsedTime(startTime));
    }

    private void loadTsiNodeAdd() {
        long startTime = System.nanoTime();

        // 연등지 노드정보 조회
        long originNodeId;
        TsiNodeVo orgTsiNodeVo;

        List<NodeAddVo> objLists = this.databaseMapper.getNodeAddInfoList();
        for (NodeAddVo obj : objLists) {
            originNodeId = obj.getOriginNodeId();
            orgTsiNodeVo = this.nodeManager.get(originNodeId);
            if (orgTsiNodeVo == null) {
                // 등록안된 Original Node 를 참조하는 경우
                log.error("Not found origin_node_id: {}", originNodeId);
                String ipAddr = "0.0.0.0";
                orgTsiNodeVo = new TsiNodeVo(originNodeId, ipAddr, true, true, true);
                orgTsiNodeVo.setCheckInstalled(true);
                orgTsiNodeVo.setRegistered(false);
                orgTsiNodeVo.setAddNodeEnabled(true);
                this.nodeManager.put(originNodeId, orgTsiNodeVo);   // 신규로 추가해 준다
            }
            else {
                orgTsiNodeVo.setAddNodeEnabled(true);
            }

            TsiNodeAddVo tsiNodeAddVo = new TsiNodeAddVo();
            tsiNodeAddVo.setOriginNodeId(originNodeId);

            for (int ii = 0; ii < obj.getAddDetailList().size(); ii++) {
                NodeAddDetailVo nodeAddDetailVo = obj.getAddDetailList().get(ii);

                Long nodeId = nodeAddDetailVo.getNodeId();

                TsiNodeAddDetailVo tsiNodeAddDetailVo = new TsiNodeAddDetailVo();
                TsiNodeVo addNodeVo = this.nodeManager.get(nodeId);
                if (addNodeVo == null) {
                    // 등록안된 Node 인 경우
                    log.error("Not found add_node_id: {}", nodeId);
                    addNodeVo = new TsiNodeVo(originNodeId, orgTsiNodeVo.getIpAddr(), orgTsiNodeVo.isSendTest(), orgTsiNodeVo.isSendNode(), orgTsiNodeVo.isSendCvim());
                    addNodeVo.setCheckInstalled(true);
                    addNodeVo.setRegistered(false);
                    this.nodeManager.put(nodeId, addNodeVo);    // 신규로 추가해 준다
                }

                tsiNodeAddDetailVo.setNodeId(nodeId);
                tsiNodeAddDetailVo.setOriginDirCode(nodeAddDetailVo.getOriginDirCode());    // 원천방향코드
                tsiNodeAddDetailVo.setAddNodeOrder(nodeAddDetailVo.getAddNodeOrder());      // 연등지 순서(1,2,3,...)
                tsiNodeAddDetailVo.setAddDirCode(nodeAddDetailVo.getAddDirCode());          // 연등지방향코드(','로구분), 배열로 저장한다.
                tsiNodeAddDetailVo.setNodeObj(addNodeVo);

                tsiNodeAddVo.getAddNodeMap().put(tsiNodeAddDetailVo.getOriginDirCode()*1000 + tsiNodeAddDetailVo.getAddNodeOrder(), tsiNodeAddDetailVo);
//                if (tsiNodeAddDetailVo.getNodeObj() != null) {
//                    //log.error("    {}, {}, {}", nodeAddVo.getOriginNodeId(), nodeId, detailVo.getNodeId());
//                }
            }

            orgTsiNodeVo.setTsiNodeAddVo(tsiNodeAddVo);

            this.nodeAddManager.put(tsiNodeAddVo.getOriginNodeId(), tsiNodeAddVo);
        }

        log.info("loadTsiNodeAdd: ..end. {} EA. {}", this.nodeAddManager.size(), TimeUtils.elapsedTime(startTime));
    }

    public void updateNodeStatusTerm(int serverId) {
        int result = this.databaseMapper.updateNodeStatusTerm(serverId);
        log.info("update node status term: {} EA", result);
    }
    public int insertAlarmOccrHs(AlarmOccrVo vo) {
        int res = 0;
        try {
            res = this.databaseMapper.insertAlarmOccrHs(vo);
        }
        catch(Exception e) {
            log.error("{}", e.getMessage());
        }
        return res;
    }
    public int updateProcessState(int flag) {
        int res = 0;
        try {
            if (flag == 0) {
                res = this.databaseMapper.updateProcessStatusStart(ApplicationRepository.processStateVo);
            }
            else if (flag == 1) {
                res = this.databaseMapper.updateProcessStatusRun(ApplicationRepository.processStateVo);
            }
            else {
                res = this.databaseMapper.updateProcessStatusStop(ApplicationRepository.processStateVo);
            }
        }
        catch(Exception e) {
            log.error("{}", e.getMessage());
        }
        return res;
    }
}
