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

import com.eactive.apim.portal.portalstatistics.entity.PortalStatistics;
import com.eactive.apim.portal.portalstatistics.entity.PortalStatisticsId;
import com.eactive.apim.portal.portalstatistics.entity.QPortalStatistics;
import com.eactive.eai.rms.data.jpa.AbstractEMSDataSerivce;
import com.querydsl.core.types.Projections;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Transactional(transactionManager = "transactionManagerForEMS")
public class PortalStatisticsService extends AbstractEMSDataSerivce<PortalStatistics, PortalStatisticsId, PortalStatisticsRepository> {

    public Map<String, List<PortalStatisticsDailyVO>> getDailyStatistics(String startDate, String endDate) {
        Map<String, List<PortalStatisticsDailyVO>> result = new HashMap<>();
        String yesterdayDate = getPreviousDay(startDate);

        // 전일자부터 오늘까지의 데이터를 한 번에 조회
        List<PortalStatisticsDailyVO> actualStats = findDailyStatisticsBetweenDates(yesterdayDate, endDate);

        // 날짜별로 통계 분류
        Map<String, List<PortalStatisticsDailyVO>> statsByDate = actualStats.stream()
                .collect(Collectors.groupingBy(vo -> vo.getStatisticsTime().substring(0, 8)));

        // 'today' 통계 (endDate 기준)
        List<PortalStatisticsDailyVO> todayStats = createEmptyHourlyStats(endDate);
        mergeStats(todayStats, statsByDate.getOrDefault(endDate, new ArrayList<>()));

        // 'yesterday' 통계
        List<PortalStatisticsDailyVO> yesterdayStats = createEmptyHourlyStats(yesterdayDate);
        mergeStats(yesterdayStats, statsByDate.getOrDefault(yesterdayDate, new ArrayList<>()));

        result.put("today", todayStats);
        result.put("yesterday", yesterdayStats);

        return result;
    }

    private List<PortalStatisticsDailyVO> findDailyStatisticsBetweenDates(String startDate, String endDate) {
        QPortalStatistics ps = QPortalStatistics.portalStatistics;

        return getJPAQueryFactory()
                .select(Projections.constructor(PortalStatisticsDailyVO.class,
                        ps.id.statisticsTime,
                        ps.requestCount.sum().longValue(),
                        ps.failureCount.sum().longValue()))
                .from(ps)
                .where(ps.id.statisticsTime.between(startDate + "000000", endDate + "235959"))
                .groupBy(ps.id.statisticsTime)
                .orderBy(ps.id.statisticsTime.asc())
                .fetch();
    }

    private List<PortalStatisticsDailyVO> createEmptyHourlyStats(String date) {
        List<PortalStatisticsDailyVO> emptyStats = new ArrayList<>();
        for (int hour = 0; hour < 24; hour++) {
            String timeStr = String.format("%s%02d0000", date, hour);
            emptyStats.add(new PortalStatisticsDailyVO(timeStr, 0L, 0L));
        }
        return emptyStats;
    }

    private void mergeStats(List<PortalStatisticsDailyVO> emptyStats, List<PortalStatisticsDailyVO> actualStats) {
        Map<String, PortalStatisticsDailyVO> actualStatsMap = actualStats.stream()
                .collect(Collectors.toMap(
                        vo -> vo.getStatisticsTime().substring(8, 10),
                        vo -> vo,
                        (v1, v2) -> v1  // In case of duplicate keys, keep the first one
                ));

        for (PortalStatisticsDailyVO emptyVO : emptyStats) {
            String hour = emptyVO.getStatisticsTime().substring(8, 10);
            PortalStatisticsDailyVO actualVO = actualStatsMap.get(hour);
            if (actualVO != null) {
                emptyVO.setRequestCount(actualVO.getRequestCount());
                emptyVO.setFailureCount(actualVO.getFailureCount());
            }
        }
    }

    private String getPreviousDay(String date) {
        LocalDate localDate = LocalDate.parse(date, DateTimeFormatter.BASIC_ISO_DATE);
        return localDate.minusDays(1).format(DateTimeFormatter.BASIC_ISO_DATE);
    }

    public List<ApiStatisticsVO> getDetailedDailyApiStatistics(LocalDate startDate, LocalDate endDate, String appId) {
        QPortalStatistics ps = QPortalStatistics.portalStatistics;

        return getJPAQueryFactory()
                .select(Projections.constructor(ApiStatisticsVO.class,
                        ps.id.apiId,
                        ps.apiName,
                        ps.id.statisticsTime.substring(0, 8).as("statisticsTime"),
                        ps.requestCount.sum().longValue(),
                        ps.failureCount.sum().longValue()))
                .from(ps)
                .where(ps.id.statisticsTime.between(
                                startDate.toString().replace("-", ""),
                                endDate.toString().replace("-", "") + "235959")
                        .and(StringUtils.isNotBlank(appId) ? ps.appId.eq(appId) : null))
                .groupBy(ps.id.apiId, ps.apiName, ps.id.statisticsTime.substring(0, 8))
                .orderBy(ps.id.statisticsTime.substring(0, 8).asc(), ps.apiName.asc())
                .fetch();
    }

    public List<AppStatisticsVO> getDetailedAppStatistics(LocalDate startDate, LocalDate endDate) {
        QPortalStatistics ps = QPortalStatistics.portalStatistics;

        return getJPAQueryFactory()
                .select(Projections.constructor(AppStatisticsVO.class,
                        ps.appId,
                        ps.appName,
                        ps.id.statisticsTime.substring(0, 8).as("statisticsTime"),
                        ps.requestCount.sum().longValue(),
                        ps.failureCount.sum().longValue()))
                .from(ps)
                .where(ps.id.statisticsTime.between(
                        startDate.toString().replace("-", ""),
                        endDate.toString().replace("-", "") + "235959"))
                .groupBy(ps.appId, ps.appName, ps.id.statisticsTime.substring(0, 8))
                .orderBy(ps.id.statisticsTime.substring(0, 8).asc(), ps.appName.asc())
                .fetch();
    }


    public List<DailyAppStatisticsVO> getDetailedDailyAppStatistics(LocalDate startDate, LocalDate endDate) {
        QPortalStatistics ps = QPortalStatistics.portalStatistics;

        return getJPAQueryFactory()
                .select(Projections.constructor(DailyAppStatisticsVO.class,
                        ps.appId,
                        ps.appName,
                        ps.id.statisticsTime.substring(0, 8).as("statisticsDate"),
                        ps.requestCount.sum().longValue(),
                        ps.failureCount.sum().longValue()))
                .from(ps)
                .where(ps.id.statisticsTime.between(
                        startDate.format(DateTimeFormatter.BASIC_ISO_DATE),
                        endDate.format(DateTimeFormatter.BASIC_ISO_DATE) + "235959"))
                .groupBy(ps.appId, ps.appName, ps.id.statisticsTime.substring(0, 8))
                .orderBy(ps.id.statisticsTime.substring(0, 8).asc(), ps.appName.asc())
                .fetch();
    }

    // 기존의 getAppList 메소드 수정 (일별 통계에서도 사용할 수 있도록)
    public List<AppComboVO> getAppList() {
        QPortalStatistics ps = QPortalStatistics.portalStatistics;

        return getJPAQueryFactory()
                .select(Projections.constructor(AppComboVO.class,
                        ps.appId,
                        ps.appName))
                .from(ps)
                .groupBy(ps.appId, ps.appName)
                .orderBy(ps.appName.asc())
                .fetch();
    }

    public List<ApiStatisticsVO> getDetailedApiStatistics(LocalDate startDate, LocalDate endDate) {
        QPortalStatistics ps = QPortalStatistics.portalStatistics;

        return getJPAQueryFactory()
                .select(Projections.constructor(ApiStatisticsVO.class,
                        ps.id.apiId,
                        ps.apiName,
                        ps.id.statisticsTime,  // 일자 정보 포함
                        ps.requestCount.sum().longValue(),
                        ps.failureCount.sum().longValue()))
                .from(ps)
                .where(ps.id.statisticsTime.between(
                        startDate.format(DateTimeFormatter.BASIC_ISO_DATE),
                        endDate.format(DateTimeFormatter.BASIC_ISO_DATE) + "235959"))
                .groupBy(ps.id.apiId, ps.apiName, ps.id.statisticsTime)
                .orderBy(ps.id.statisticsTime.asc(), ps.apiName.asc())
                .fetch();
    }

    public List<OrgStatisticsVO> getDetailedOrgStatistics(LocalDate startDate, LocalDate endDate) {
        QPortalStatistics ps = QPortalStatistics.portalStatistics;

        return getJPAQueryFactory()
                .select(Projections.constructor(OrgStatisticsVO.class,
                        ps.orgId,
                        ps.orgName,
                        ps.id.statisticsTime.substring(0, 8).as("statisticsTime"),
                        ps.requestCount.sum().longValue(),
                        ps.failureCount.sum().longValue()))
                .from(ps)
                .where(ps.id.statisticsTime.between(
                        startDate.toString().replace("-", ""),
                        endDate.toString().replace("-", "") + "235959"))
                .groupBy(ps.orgId, ps.orgName, ps.id.statisticsTime.substring(0, 8))
                .orderBy(ps.id.statisticsTime.substring(0, 8).asc(), ps.orgName.asc())
                .fetch();
    }

    public List<DailyOrgStatisticsVO> getDetailedDailyOrgStatistics(LocalDate startDate, LocalDate endDate) {
        QPortalStatistics ps = QPortalStatistics.portalStatistics;

        return getJPAQueryFactory()
                .select(Projections.constructor(DailyOrgStatisticsVO.class,
                        ps.orgId,
                        ps.orgName,
                        ps.id.statisticsTime.substring(0, 8).as("statisticsDate"),
                        ps.requestCount.sum().longValue(),
                        ps.failureCount.sum().longValue()))
                .from(ps)
                .where(ps.id.statisticsTime.between(
                        startDate.format(DateTimeFormatter.BASIC_ISO_DATE),
                        endDate.format(DateTimeFormatter.BASIC_ISO_DATE) + "235959"))
                .groupBy(ps.orgId, ps.orgName, ps.id.statisticsTime.substring(0, 8))
                .orderBy(ps.id.statisticsTime.substring(0, 8).asc(), ps.orgName.asc())
                .fetch();
    }
}
