package com.utic.center.utic.traf.server.service.worker;

import com.utic.center.common.spring.SpringUtils;
import com.utic.center.common.utils.LogUtils;
import com.utic.center.utic.traf.server.dto.LinkCollectDto;
import com.utic.center.utic.traf.server.dto.LinkCollectParam;
import com.utic.center.utic.traf.server.dto.LinkCollectResultDto;
import com.utic.center.utic.traf.server.dto.LinkCollectSetupDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@RequiredArgsConstructor
public class LinkTrafCollectWorker implements Runnable {

    private final LinkCollectSetupDto clctStup;
    private final LinkCollectParam params;
    private final ConcurrentHashMap<String, LinkCollectResultDto> mapData;

    @Override
    public void run() {
        final long start = System.currentTimeMillis();
        final String clctSystCd = this.clctStup.getClctSystCd();
        final String jobName = this.clctStup.getClctSystNm();

        SqlSessionFactory sqlSessionFactory;

        this.clctStup.setClctNum(0);

        int selectCount = 0;
        List<LinkCollectDto> result = new ArrayList<>();
        List<LinkCollectDto> queryResult = new ArrayList<>();

        try {
            sqlSessionFactory = (SqlSessionFactory)SpringUtils.getBean(clctSystCd.toLowerCase());
        } catch (Exception e) {
            sqlSessionFactory = SpringUtils.getBean(SqlSessionFactory.class);
        }

        if (sqlSessionFactory == null) {
            log.error("[ERR] LinkTrafCollectWorker.run: SqlSessionFactory is null. [{}], {}.", clctSystCd, jobName);
            this.mapData.put(clctSystCd, LinkCollectResultDto.builder().clctSystCd(clctSystCd).lists(result).build());
            return;
        }

        try {
            String queryId = "findAllLinkTraf" + clctSystCd;
            MappedStatement mappedStatement = sqlSessionFactory.getConfiguration().getMappedStatement(queryId);
            if (mappedStatement == null) {
                log.error("[ERR] LinkTrafCollectWorker.run: [{}], {}, {}, queryId is not found.", clctSystCd, jobName, queryId);
                this.mapData.put(clctSystCd, LinkCollectResultDto.builder().clctSystCd(clctSystCd).lists(result).build());
                return;
            }

            // 쿼리 ID가 존재하는 경우에만 실행
            SqlSession sqlSession = null;
            try {
                sqlSession = sqlSessionFactory.openSession(ExecutorType.SIMPLE, false);
                queryResult = sqlSession.selectList(queryId, params);
            } catch (Exception e) {
                log.error("[ERR] LinkTrafCollectWorker.run: Exception: [{}], {}, {}, {}", clctSystCd, jobName, queryId, e.getMessage());
            }
            finally {
                if (sqlSession != null) {
                    sqlSession.close();
                }
            }

            if (queryResult == null) {
                this.mapData.put(clctSystCd, LinkCollectResultDto.builder().clctSystCd(clctSystCd).lists(result).build());
            }
            else {
                selectCount = queryResult.size();

                // 수집의 경우 LinkId 가 중복될 수 있으므로
                // 중복된 LinkId 를 제거하기 위해 HashSet 을 사용하여 중복값을 제거
                Set<String> uniqueSet = new HashSet<>();
                for (LinkCollectDto dto : queryResult) {
                    if (uniqueSet.add(dto.getLinkId())) {  // HashSet 의 add() 메서드는 중복값이 있으면 false 를 반환
                        result.add(dto);
                    }
                }
                this.mapData.put(clctSystCd, LinkCollectResultDto.builder().clctSystCd(clctSystCd).lists(result).build());
            }
        } catch (Exception e) {
            log.error("[ERR] LinkTrafCollectWorker.run: Exception, [{}], {}, {}.", clctSystCd, jobName, e.getMessage());
            this.mapData.put(clctSystCd, LinkCollectResultDto.builder().clctSystCd(clctSystCd).lists(result).build());
        }

        this.clctStup.setClctNum(result.size());

        log.info("[INF] {}: SET: {} EA. | {}",
                LogUtils.elapsedLog(jobName, selectCount, System.currentTimeMillis() - start),
                LogUtils.numberLog(result.size()),
                Thread.currentThread().getName());
    }

}
