package com.eactive.eai.rms.data.entity.onl.apim.obp;

import com.eactive.eai.data.entity.onl.message.EAIMessageEntity;
import com.eactive.eai.data.entity.onl.message.QEAIMessageEntity;
import com.eactive.eai.data.entity.onl.unifbwk.QUnifBwkTp;
import com.querydsl.core.Tuple;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * ObpGwMetric JPA DAO - JPA/QueryDSL을 사용한 로그 테이블 집계
 *
 * <p>Multi-tenancy 지원: 호출 전 DataSourceContextHolder 설정 필요</p>
 */
@Repository
public class ObpGwMetricJpaDAO {

    private static final Logger log = LoggerFactory.getLogger(ObpGwMetricJpaDAO.class);

    @PersistenceContext
    private EntityManager entityManager;

    private JPAQueryFactory getQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }

    /**
     * EAIBZWKDSTCD 코드 목록 조회 (TSEAICM01)
     *
     * @return 업무구분코드 목록
     */
    public List<String> selectAllBzwkCodes() {
        QUnifBwkTp cm01 = QUnifBwkTp.unifBwkTp;

        return getQueryFactory()
                .select(cm01.eaibzwkdstcd)
                .from(cm01)
                .orderBy(cm01.eaibzwkdstcd.asc())
                .fetch();
    }

    /**
     * EAIBZWKDSTCD별 시간대 로그 집계 (Native Query)
     *
     * <p>동적 테이블명(TSEAILG1X)을 사용하므로 Native Query 필요</p>
     *
     * @param tableName  동적 테이블명 (예: TSEAILG14)
     * @param bzwkCode   업무구분코드
     * @param startTime  시작시간 (yyyyMMddHHmmssSSS)
     * @param endTime    종료시간 (yyyyMMddHHmmssSSS)
     * @param timeslice  타임슬라이스 (yyyyMMddHH0000000)
     * @return 집계 결과 목록
     */
    @SuppressWarnings("unchecked")
    public List<ObpGwMetricVO> selectHourlyMetricsByBzwkCode(
            String tableName, String bzwkCode, String startTime, String endTime, String timeslice) {

        String sql = "SELECT " +
                "EAISVCNAME AS API_ID, " +
                "CLIENTID AS CLIENT_ID, " +
                "EAISEVRINSTNCNAME AS HOSTNAME, " +
                "ORGID AS ORG_ID, " +
                ":timeslice AS TIMESLICE, " +
                "SUM(CASE WHEN LOGPRCSSSERNO = '100' THEN 1 ELSE 0 END) AS ATTEMPTED_COUNT, " +
                "SUM(CASE WHEN LOGPRCSSSERNO = '400' " +
                "         AND (RSPNSERRCDNAME IS NULL OR RSPNSERRCDNAME LIKE 'ESB%') " +
                "    THEN 1 ELSE 0 END) AS COMPLETED_COUNT, " +
                "MAX(TRIM(EAISVCSERNO)) AS EAI_SVC_SERNO " +
                "FROM " + tableName + " " +
                "WHERE EAIBZWKDSTCD = :bzwkCode " +
                "  AND MSGDPSTYMS BETWEEN :startTime AND :endTime " +
                "GROUP BY EAISVCNAME, CLIENTID, EAISEVRINSTNCNAME, ORGID " +
                "HAVING SUM(CASE WHEN LOGPRCSSSERNO = '100' THEN 1 ELSE 0 END) > 0 " +
                "    OR SUM(CASE WHEN LOGPRCSSSERNO = '400' THEN 1 ELSE 0 END) > 0";

        Query query = entityManager.createNativeQuery(sql);
        query.setParameter("timeslice", timeslice);
        query.setParameter("bzwkCode", bzwkCode);
        query.setParameter("startTime", startTime);
        query.setParameter("endTime", endTime);

        List<Object[]> results = query.getResultList();

        return results.stream()
                .map(this::mapToObpGwMetricVO)
                .collect(Collectors.toList());
    }

    /**
     * Native Query 결과를 ObpGwMetricVO로 변환
     */
    private ObpGwMetricVO mapToObpGwMetricVO(Object[] row) {
        ObpGwMetricVO vo = new ObpGwMetricVO();
        vo.setApiId((String) row[0]);
        vo.setClientId((String) row[1]);
        vo.setHostname((String) row[2]);
        vo.setOrgId((String) row[3]);
        vo.setTimeslice((String) row[4]);
        vo.setAttemptedCount(toLong(row[5]));
        vo.setCompletedCount(toLong(row[6]));
        vo.setEaiSvcSerno((String) row[7]);
        return vo;
    }

    /**
     * Number 타입을 Long으로 변환 (Oracle은 BigDecimal 반환)
     */
    private Long toLong(Object value) {
        if (value == null) return 0L;
        if (value instanceof BigDecimal) {
            return ((BigDecimal) value).longValue();
        }
        if (value instanceof Number) {
            return ((Number) value).longValue();
        }
        return 0L;
    }

    /**
     * API 이름 조회 (TSEAIHE01)
     *
     * @param apiIds API ID 목록 (EAISVCNAME)
     * @return API ID → API 이름 맵
     */
    public Map<String, String> selectApiNames(Set<String> apiIds) {
        if (apiIds == null || apiIds.isEmpty()) {
            return new HashMap<>();
        }

        QEAIMessageEntity he01 = QEAIMessageEntity.eAIMessageEntity;

        List<Tuple> results = getQueryFactory()
                .select(he01.eaisvcname, he01.eaisvcdesc)
                .from(he01)
                .where(he01.eaisvcname.in(apiIds))
                .fetch();

        Map<String, String> result = new HashMap<>();
        for (Tuple tuple : results) {
            String apiId = tuple.get(he01.eaisvcname);
            String apiName = tuple.get(he01.eaisvcdesc);
            if (apiId != null && apiName != null) {
                result.put(apiId, apiName);
            }
        }
        return result;
    }

    /**
     * URI 및 HTTP Method 조회 결과
     */
    public static class UriMethodResult {
        private final Map<String, String> uriMap;
        private final Map<String, String> methodMap;

        public UriMethodResult(Map<String, String> uriMap, Map<String, String> methodMap) {
            this.uriMap = uriMap;
            this.methodMap = methodMap;
        }

        public Map<String, String> getUriMap() {
            return uriMap;
        }

        public Map<String, String> getMethodMap() {
            return methodMap;
        }
    }

    /**
     * URI 및 HTTP Method 조회 (HTTP_ADAPTER_EXTRA_LOG)
     *
     * <p>SERVICE_PROCESS_NUMBER = 100 (인바운드 요청) 조건으로 조회</p>
     *
     * @param eaiSvcSernos EAI 서비스 일련번호 목록 (GUID)
     * @return UriMethodResult (eaiSvcSerno → URI 맵, eaiSvcSerno → METHOD 맵)
     */
    @SuppressWarnings("unchecked")
    public UriMethodResult selectUrisAndMethods(Set<String> eaiSvcSernos) {
        if (eaiSvcSernos == null || eaiSvcSernos.isEmpty()) {
            return new UriMethodResult(new HashMap<>(), new HashMap<>());
        }

        // IN절 파라미터 바인딩을 위한 처리
        String sql = "SELECT GUID AS EAI_SVC_SERNO, URL AS URI, HTTP_METHOD AS METHOD " +
                "FROM HTTP_ADAPTER_EXTRA_LOG " +
                "WHERE SERVICE_PROCESS_NUMBER = 100 " +
                "  AND GUID IN (:eaiSvcSernos)";

        Query query = entityManager.createNativeQuery(sql);
        query.setParameter("eaiSvcSernos", new ArrayList<>(eaiSvcSernos));

        List<Object[]> results = query.getResultList();

        Map<String, String> uriMap = new HashMap<>();
        Map<String, String> methodMap = new HashMap<>();

        for (Object[] row : results) {
            String eaiSvcSerno = (String) row[0];
            String uri = (String) row[1];
            String method = (String) row[2];
            if (eaiSvcSerno != null) {
                if (uri != null) {
                    uriMap.put(eaiSvcSerno, uri);
                }
                if (method != null) {
                    methodMap.put(eaiSvcSerno, method);
                }
            }
        }

        return new UriMethodResult(uriMap, methodMap);
    }
}
