package com.eactive.eai.rms.ext.djb.statistics;

import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.eactive.eai.data.jpa.AbstractDataService;
import com.eactive.eai.rms.common.util.StringUtils;
import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsDay;
import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsDayId;
import com.eactive.ext.kjb.statistics.ui.ApiStatsSearch;
import com.eactive.ext.kjb.statistics.ui.ApiStatsUI;

@Service
@Transactional
public class ApiUseStatsService
		extends AbstractDataService<ApiStatsDay, ApiStatsDayId, ApiUseStatsRepository> {

	private static final int MAX_DAYS = 31;

	@PersistenceContext
	private EntityManager entityManager;

	@SuppressWarnings("unchecked")
	public Page<ApiStatsUI> selectList(ApiStatsSearch search, Pageable pageable) {
		Query dataQuery = getDataQuery(search);
		dataQuery.setFirstResult((int) pageable.getOffset());
		dataQuery.setMaxResults(pageable.getPageSize());

		List<Object[]> rows = dataQuery.getResultList();
		List<ApiStatsUI> results = rows.stream()
						                .map(this::toVO)
						                .collect(Collectors.toList());
		
		long records = rows.size() > 0 ? StringUtils.toLong(rows.get(0)[0]) : 0;
				
		return new PageImpl<>(results, pageable, records);
	}
	
	
	@SuppressWarnings("unchecked")
	public List<ApiStatsUI> selectList(ApiStatsSearch search) {
		Query dataQuery = getDataQuery(search);
		List<Object[]> rows = dataQuery.getResultList();
		return rows.stream()
                .map(this::toVO)
                .collect(Collectors.toList());
	}
	
		
	private Query getDataQuery(ApiStatsSearch search) {
		
		StringBuilder where = buildNativeWhere(search);
		
		String dataSql = "";
		if ("ORG".equals(search.getSearchType())) {
			dataSql = "SELECT COUNT(*) OVER () AS RECORDS "
				+ ", B.ORGNAME"
				+ ", SUM(A.TOTAL_CNT)"
				+ ", SUM(A.SUCCESS_CNT)"
				+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN SUM(A.SUCCESS_CNT) * 100 / SUM(A.TOTAL_CNT) ELSE NULL END"
				+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN (SUM(A.TOTAL_CNT) - SUM(A.SUCCESS_CNT)) * 100 / SUM(A.TOTAL_CNT) ELSE NULL END"
				+ ", SUM(A.TIMEOUT_CNT)"
				+ ", SUM(A.SYSTEM_ERR_CNT)"
				+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN TRUNC(SUM(A.TOTAL_CNT * A.AVG_RESP_TIME) / SUM(A.TOTAL_CNT)) ELSE NULL END"
				+ ", MIN(A.MIN_RESP_TIME)"
				+ ", MAX(A.MAX_RESP_TIME)"
				+ " FROM API_STATS_DAY A"
				+ " LEFT OUTER JOIN TSEAIAU01 B ON A.CLIENT_ID = B.CLIENTID"
				+ " LEFT OUTER JOIN TSEAIHE01 C ON A.API_NAME  = C.EAISVCNAME "
				+ where
				+ " GROUP BY B.ORGNAME"
				+ " ORDER BY ORGNAME";
		} else if ("API".equals(search.getSearchType())) {
			dataSql = "SELECT COUNT(*) OVER () AS RECORDS "
					+ ", A.API_NAME"
					+ ", SUM(A.TOTAL_CNT)"
					+ ", SUM(A.SUCCESS_CNT)"
					+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN SUM(A.SUCCESS_CNT) * 100 / SUM(A.TOTAL_CNT) ELSE NULL END"
					+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN (SUM(A.TOTAL_CNT) - SUM(A.SUCCESS_CNT)) * 100 / SUM(A.TOTAL_CNT) ELSE NULL END"
					+ ", SUM(A.TIMEOUT_CNT)"
					+ ", SUM(A.SYSTEM_ERR_CNT)"
					+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN TRUNC(SUM(A.TOTAL_CNT * A.AVG_RESP_TIME) / SUM(A.TOTAL_CNT)) ELSE NULL END"
					+ ", MIN(A.MIN_RESP_TIME)"
					+ ", MAX(A.MAX_RESP_TIME)"
					+ " FROM API_STATS_DAY A "
					+ " LEFT OUTER JOIN TSEAIAU01 B ON A.CLIENT_ID = B.CLIENTID"
					+ " LEFT OUTER JOIN TSEAIHE01 C ON A.API_NAME  = C.EAISVCNAME "
					+ where
					+ " GROUP BY A.API_NAME"
					+ " ORDER BY API_NAME";
		} else if ("DATE".equals(search.getSearchType())) {
			dataSql = "SELECT COUNT(*) OVER () AS RECORDS "
					+ ", TO_CHAR(STAT_TIME,'YYYY-MM-DD') AS STAT_TIME "
					+ ", SUM(A.TOTAL_CNT)"
					+ ", SUM(A.SUCCESS_CNT)"
					+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN SUM(A.SUCCESS_CNT) * 100 / SUM(A.TOTAL_CNT) ELSE NULL END"
					+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN (SUM(A.TOTAL_CNT) - SUM(A.SUCCESS_CNT)) * 100 / SUM(A.TOTAL_CNT) ELSE NULL END"
					+ ", SUM(A.TIMEOUT_CNT)"
					+ ", SUM(A.SYSTEM_ERR_CNT)"
					+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN TRUNC(SUM(A.TOTAL_CNT * A.AVG_RESP_TIME) / SUM(A.TOTAL_CNT)) ELSE NULL END"
					+ ", MIN(A.MIN_RESP_TIME)"
					+ ", MAX(A.MAX_RESP_TIME)"
					+ " FROM API_STATS_DAY A "
					+ " LEFT OUTER JOIN TSEAIAU01 B ON A.CLIENT_ID = B.CLIENTID"
					+ " LEFT OUTER JOIN TSEAIHE01 C ON A.API_NAME  = C.EAISVCNAME "
					+ where
					+ " GROUP BY TO_CHAR(STAT_TIME,'YYYY-MM-DD')"
					+ " ORDER BY STAT_TIME";
		}
		
		Query dataQuery = entityManager.createNativeQuery(dataSql);
		if (search.getParsedStartDate() != null) {
			dataQuery.setParameter("searchStartDate", search.getParsedStartDate());
			dataQuery.setParameter("searchEndDate", search.getParsedEndDate());
		}
		if (StringUtils.isNotBlank(search.getSearchOrgName())) {
			dataQuery.setParameter("searchOrgName", "%" + search.getSearchOrgName().toUpperCase() + "%");
		}
		if (StringUtils.isNotBlank(search.getSearchApiName())) {
			dataQuery.setParameter("searchApiName", "%" + search.getSearchApiName().toUpperCase() + "%");
		}
		return dataQuery;
	}
	

	/**
     * Native Query 결과를 ApiStatsUI로 변환
     */
    private ApiStatsUI toVO(Object[] row) {
    	ApiStatsUI vo = new ApiStatsUI();
		vo.setOrgName(StringUtils.toString(row[1]));
		vo.setTotalCnt(StringUtils.toLong(row[2]));
		vo.setSuccessCnt(StringUtils.toLong(row[3]));
		vo.setSuccessRate(StringUtils.toDecimal(row[4]).setScale(2, RoundingMode.HALF_UP));
		vo.setFailRate(StringUtils.toDecimal(row[5]).setScale(2, RoundingMode.HALF_UP));
		vo.setTimeoutCnt(StringUtils.toLong(row[6]));
		vo.setSystemErrCnt(StringUtils.toLong(row[7]));
		vo.setAvgRespTime(StringUtils.toDecimal(row[8]));
		vo.setMinRespTime(StringUtils.toDecimal(row[9]));
		vo.setMaxRespTime(StringUtils.toDecimal(row[10]));
        return vo;
    }
	

	/**
	 * native SQL용 동적 WHERE 절 생성
	 */
	private StringBuilder buildNativeWhere(ApiStatsSearch search) {
		StringBuilder where = new StringBuilder(" WHERE 1=1");

		calculateDateRange(search);
		
		if (search.getParsedStartDate() != null) {
			where.append(" AND A.STAT_TIME >= :searchStartDate ");
			where.append(" AND A.STAT_TIME <= :searchEndDate ");
		}

		if (StringUtils.isNotBlank(search.getSearchOrgName())) {
			where.append(" AND UPPER(B.ORGNAME) LIKE :searchOrgName ");
		}

		if (StringUtils.isNotBlank(search.getSearchApiName())) {
			where.append(" AND UPPER(C.EAISVCDESC) LIKE :searchApiName ");
		}

		return where;
	}


	/**
	 * 조회 기간 계산 (최대 31일 제한)
	 */
	private void calculateDateRange(ApiStatsSearch search) {
		if (StringUtils.isBlank(search.getSearchStartDateTime())) {
			return;
		}

		LocalDate startDate = LocalDate.parse(search.getSearchStartDateTime(),
				DateTimeFormatter.ofPattern("yyyyMMdd"));
		LocalDate endDate;

		if (StringUtils.isNotBlank(search.getSearchEndDateTime())) {
			endDate = LocalDate.parse(search.getSearchEndDateTime(),
					DateTimeFormatter.ofPattern("yyyyMMdd"));
			if (java.time.Period.between(startDate, endDate).getDays() > MAX_DAYS) {
				endDate = startDate.plusDays(MAX_DAYS);
			}
		} else {
			endDate = startDate.plusDays(MAX_DAYS);
		}

		search.setParsedStartDate(startDate);
		search.setParsedEndDate(endDate);
	}
	
}
