package com.caliverse.admin.domain.service;

import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import com.caliverse.admin.Indicators.Indicatorsservice.aggregationservice.*;
import com.caliverse.admin.Indicators.entity.*;
import com.caliverse.admin.logs.logservice.indicators.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.caliverse.admin.domain.entity.Currencys;
import com.caliverse.admin.domain.entity.ROUTE;
import com.caliverse.admin.domain.response.IndicatorsResponse;
import com.caliverse.admin.global.common.code.CommonCode;
import com.caliverse.admin.global.common.code.ErrorCode;
import com.caliverse.admin.global.common.exception.RestApiException;
import com.caliverse.admin.global.common.utils.CommonUtils;
import com.caliverse.admin.global.common.utils.ExcelUtils;

import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
@Slf4j
public class IndicatorsService {
    private final ExcelUtils excelUtils;


    private IndicatorsAuLoadService indicatorsAuLoadService;
    private final IndicatorsDauLoadService indicatorsDauLoadService;
    private final IndicatorsMcuLoadService indicatorsMcuLoadService;
    private final IndicatorsDglcLoadService indicatorsDglcLoadService;
    private final IndicatorsPlayTimeLoadService indicatorsPlayTimeLoadService;
    private final IndicatorsNruLoadService indicatorsNruLoadService;
    private final IndicatorsCapacityLoadService indicatorsCapacityLoadService;
    private final IndicatorsMauLoadService indicatorsMauLoadService;
    private final IndicatorsWauLoadService indicatorsWauLoadService;
    private final IndicatorsUgqCreateLoadService indicatorsUgqCreateLoadService;
    private final IndicatorsMetaverServerLoadService indicatorsMetaverServerLoadService;

    private final IndicatorsDauService dauService;
    private final IndicatorsNruService indicatorsNruService;
    private final IndicatorsMcuService mcuService;

    private final DynamoDBMetricsService dynamoDBMetricsService;

    //UserStatistics
    public IndicatorsResponse list(Map<String, String> requestParams){

        String startDt = requestParams.get("start_dt");
        String endDt = requestParams.get("end_dt");
        log.info("list get UserStatistics startDt: {}, endDt: {}", startDt, endDt);

        List<IndicatorsResponse.userStatistics> userStatistics = getMergeUserStatisticsData(startDt, endDt);

         return IndicatorsResponse.builder()
                 .resultData(IndicatorsResponse.ResultData.builder()
                         .userStatisticsList(userStatistics)
                         .build())
                 .status(CommonCode.SUCCESS.getHttpStatus())
                 .result(CommonCode.SUCCESS.getResult())
                 .build();

    }

    // dashboard
    public IndicatorsResponse total(){
        LocalDate search_date = LocalDate.now().minusDays(1);
        DauLogInfo dau_info = indicatorsDauLoadService.getDailyIndicatorLog(search_date.toString(), DauLogInfo.class);
        NruLogInfo nru_info = indicatorsNruLoadService.getDailyIndicatorLog(search_date.toString(), NruLogInfo.class);
        McuLogInfo mcu_info = indicatorsMcuLoadService.getDailyIndicatorLog(search_date.toString(), McuLogInfo.class);

        LocalDate pre_search_date = LocalDate.now().minusDays(2);
        DauLogInfo pre_dau_info = indicatorsDauLoadService.getDailyIndicatorLog(pre_search_date.toString(), DauLogInfo.class);
        NruLogInfo pre_nru_info = indicatorsNruLoadService.getDailyIndicatorLog(pre_search_date.toString(), NruLogInfo.class);
        McuLogInfo pre_mcu_info = indicatorsMcuLoadService.getDailyIndicatorLog(pre_search_date.toString(), McuLogInfo.class);

        ObjectMapper objectMapper = new ObjectMapper();
        try {
            log.info("total pre_dau: {}, dau: {}, pre_nru: {}, nru: {}, pre_mcu: {}, mcu: {}",
                    objectMapper.writeValueAsString(pre_dau_info), objectMapper.writeValueAsString(dau_info),
                    objectMapper.writeValueAsString(pre_nru_info), objectMapper.writeValueAsString(nru_info),
                    objectMapper.writeValueAsString(pre_mcu_info), objectMapper.writeValueAsString(mcu_info));
        }catch (JsonProcessingException e) {
            log.error("Error converting object to JSON", e);
        }

        int dau_cnt = dau_info == null ? 0 : dau_info.getDau();
        int pre_dau_cnt = pre_dau_info == null ? 0 : pre_dau_info.getDau();
        IndicatorsResponse.Dau dau = IndicatorsResponse.Dau.builder()
                .count(dau_cnt)
                .updown(CommonUtils.getDiffStatus(dau_cnt, pre_dau_cnt))
                .dif(CommonUtils.getDiffRate(dau_cnt, pre_dau_cnt))
                .build();

        int nru_cnt = nru_info == null ? 0 : nru_info.getNru();
        int pre_nru_cnt = pre_nru_info == null ? 0 : pre_nru_info.getNru();
        IndicatorsResponse.NRU nru = IndicatorsResponse.NRU.builder()
                .count(nru_cnt)
                .updown(CommonUtils.getDiffStatus(nru_cnt, pre_nru_cnt))
                .dif(CommonUtils.getDiffRate(nru_cnt, pre_nru_cnt))
                .build();

        int mcu_cnt = mcu_info == null ? 0 : mcu_info.getMaxCountUser();
        int pre_mcu_cnt = pre_mcu_info == null ? 0 : pre_mcu_info.getMaxCountUser();
        IndicatorsResponse.MCU mcu = IndicatorsResponse.MCU.builder()
                .count(mcu_cnt)
                .updown(CommonUtils.getDiffStatus(mcu_cnt, pre_mcu_cnt))
                .dif(CommonUtils.getDiffRate(mcu_cnt, pre_mcu_cnt))
                .build();

        IndicatorsResponse.Dashboard dashboard = IndicatorsResponse.Dashboard.builder()
                .dau(dau)
                .mcu(mcu)
                .pu(null)
                .nru(nru)
                .build();

         return IndicatorsResponse.builder()
                 .status(CommonCode.SUCCESS.getHttpStatus())
                 .result(CommonCode.SUCCESS.getResult())
                 .resultData(IndicatorsResponse.ResultData.builder()
                         .dashboard(dashboard)
                         .build()
                 )
                 .build();
    }

    //유저 지표 엑셀 다운로드(현재 frontend 기능으로 사용)
    public void userExcelDown(Map<String, String> requestParams, HttpServletResponse res){

        LocalDate startDt = LocalDate.parse(CommonUtils.objectToString(requestParams.get("start_dt")));
        LocalDate endDt = LocalDate.parse(CommonUtils.objectToString(requestParams.get("end_dt")));
//         List<Distinct> distinctCount = getDistinctCount(startDt, endDt);
        // String sheetName = "Caliverse_User";

        // String headerNames[] = new String[]{"일자", "DAU", "WAU", "MAU","NRU","PU","MCU"};

        // String[][] bodyDatass = new String[distinctCount.size()][7];

        // for (int i = 0; i < distinctCount.size(); i++) {
        //     bodyDatass[i][0] = String.valueOf(distinctCount.get(i).getDate());
        //     bodyDatass[i][1] = String.valueOf(distinctCount.get(i).getDau());
        //     bodyDatass[i][2] = String.valueOf(distinctCount.get(i).getWau());
        //     bodyDatass[i][3] = String.valueOf(distinctCount.get(i).getMau());
        //     bodyDatass[i][4] = String.valueOf(0);
        //     bodyDatass[i][5] = String.valueOf(0);
        //     bodyDatass[i][6] = String.valueOf(0);
        // }

        // String outfileName = "Caliverse_User";
        // try {
        //     excelUtils.excelDownload(sheetName, headerNames, bodyDatass ,outfileName, res);
        // }catch (IOException exception){
        //     logger.error(exception.getMessage());
        // }

    }
    // 유저 지표 Retention
    public IndicatorsResponse retentionList(Map<String, String> requestParams){
        List<IndicatorsResponse.Retention> retentionList = new ArrayList<>();
        String startDt = CommonUtils.objectToString(requestParams.get("start_dt"));
        String endDt = CommonUtils.objectToString(requestParams.get("end_dt"));
        List<LocalDate> dateRange = CommonUtils.dateRange(startDt, endDt);
        int cnt = dateRange.size();
        for (LocalDate date : dateRange) {
            List<IndicatorsResponse.Dday> dDayList = new ArrayList<>();
            for (int i = 0; i < cnt; i++){
                IndicatorsResponse.Dday dDay = IndicatorsResponse.Dday.builder()
                        .date("D+" + i)
                        .dif("0%")
                        .build();
                dDayList.add(dDay);
            }
            cnt -- ;
            IndicatorsResponse.Retention retention = IndicatorsResponse.Retention.builder()
                    .date(date)
                    .dDay(dDayList)
                    .build();
            retentionList.add(retention);
        }
        return IndicatorsResponse.builder()
                .resultData(IndicatorsResponse.ResultData.builder()
                        .retentionList(retentionList)
                        .build())
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();

    }
    public void retentionExcelDown(Map<String, String> requestParams,HttpServletResponse res){
        List<IndicatorsResponse.Retention> retentionList = new ArrayList<>();
        String startDt = CommonUtils.objectToString(requestParams.get("start_dt"));
        String endDt = CommonUtils.objectToString(requestParams.get("end_dt"));
        List<LocalDate> dateRange = CommonUtils.dateRange(startDt, endDt);
        int cnt = dateRange.size();
        for (LocalDate date : dateRange) {
            List<IndicatorsResponse.Dday> dDayList = new ArrayList<>();
            for (int i = 0; i < cnt; i++){
                IndicatorsResponse.Dday dDay = IndicatorsResponse.Dday.builder()
                        .date("D+" + i)
                        .dif("0%")
                        .build();
                dDayList.add(dDay);
            }
            cnt -- ;
            IndicatorsResponse.Retention retention = IndicatorsResponse.Retention.builder()
                    .date(date)
                    .dDay(dDayList)
                    .build();
            retentionList.add(retention);
        }
        // 엑셀 파일 생성 및 다운로드
        try {
            ExcelUtils.exportToExcelByRentention(retentionList,res);
        }catch (IOException exception){
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.ERROR_EXCEL_DOWN.getMessage());
        }

    }
    // 유저 지표 Retention
    public IndicatorsResponse segmentList(Map<String, String> requestParams){
        // LocalDate searchDt = LocalDate.parse(CommonUtils.objectToString(requestParams.get("search_dt")));

        // List<IndicatorsResponse.Segment> segmentList = Arrays.asList(
        //         IndicatorsResponse.Segment.builder().type("Excellent").au("0").dif("0%").build(),
        //         IndicatorsResponse.Segment.builder().type("Influential").au("0").dif("0%").build(),
        //         IndicatorsResponse.Segment.builder().type("Potential").au("0").dif("0%").build(),
        //         IndicatorsResponse.Segment.builder().type("Before leave").au("0").dif("0%").build(),
        //         IndicatorsResponse.Segment.builder().type("InActive").au("0").dif("0%").build(),
        //         IndicatorsResponse.Segment.builder().type("New Join").au("0").dif("0%").build()
        // );
        // return IndicatorsResponse.builder()
        //         .resultData(IndicatorsResponse.ResultData.builder()
        //                 .segmentList(segmentList)
        //                 .startDt(searchDt.minusDays(100))
        //                 .endDt(searchDt)
        //                 .build())
        //         .status(CommonCode.SUCCESS.getHttpStatus())
        //         .result(CommonCode.SUCCESS.getResult())
        //         .build();
        return null;

    }
    public void segmentExcelDown(Map<String, String> requestParams, HttpServletResponse res){
        LocalDate searchDt = LocalDate.parse(CommonUtils.objectToString(requestParams.get("search_dt")));

        List<IndicatorsResponse.Segment> segmentList = Arrays.asList(
                IndicatorsResponse.Segment.builder().type("Excellent").au("0").dif("0%").build(),
                IndicatorsResponse.Segment.builder().type("Influential").au("0").dif("0%").build(),
                IndicatorsResponse.Segment.builder().type("Potential").au("0").dif("0%").build(),
                IndicatorsResponse.Segment.builder().type("Before leave").au("0").dif("0%").build(),
                IndicatorsResponse.Segment.builder().type("InActive").au("0").dif("0%").build(),
                IndicatorsResponse.Segment.builder().type("New Join").au("0").dif("0%").build()
        );

        LocalDate startDt = searchDt.minusDays(100);
        LocalDate endDt = searchDt;
        // 엑셀 파일 생성 및 다운로드
        try {
            ExcelUtils.exportToExcelBySegment(segmentList,startDt,endDt,res);
        }catch (IOException exception){
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.ERROR_EXCEL_DOWN.getMessage());
        }

    }
    // 유저 지표 플레이타임
    public IndicatorsResponse playTimeList(Map<String, String> requestParams){
        String startDt = CommonUtils.objectToString(requestParams.get("start_dt"));
        String endDt = CommonUtils.objectToString(requestParams.get("end_dt"));
        List<LocalDate> dateRange = CommonUtils.dateRange(startDt, endDt);
        List<IndicatorsResponse.Playtime> playtimeList = new ArrayList<>();
        for (LocalDate date : dateRange){
            IndicatorsResponse.Playtime build = IndicatorsResponse.Playtime.builder()
                    .date(date)
                    .totalTime(0)
                    .averageTime(0)
                    .userCnt(Arrays.asList(0, 0, 0, 0))
                    .build();
            playtimeList.add(build);
        }
        return IndicatorsResponse.builder()
                .resultData(IndicatorsResponse.ResultData.builder()
                        .playtimeList(playtimeList)
                        .build())
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();

    }
    public void playTimeExcelDown(Map<String, String> requestParams, HttpServletResponse res){
        String startDt = CommonUtils.objectToString(requestParams.get("start_dt"));
        String endDt = CommonUtils.objectToString(requestParams.get("end_dt"));
        List<LocalDate> dateRange = CommonUtils.dateRange(startDt, endDt);
        List<IndicatorsResponse.Playtime> playtimeList = new ArrayList<>();
        for (LocalDate date : dateRange){
            IndicatorsResponse.Playtime build = IndicatorsResponse.Playtime.builder()
                    .date(date)
                    .totalTime(0)
                    .averageTime(0)
                    .userCnt(Arrays.asList(0, 0, 0, 0))
                    .build();
            playtimeList.add(build);
        }

        String sheetName = "PlayTime";

        String headerNames[] = new String[]{"일자","30분 이내","30분~1시간", "1시간~3시간", "3시간 이상"
                , "총 누적 플레이타임(분)","1인당 평균 플레이타임(분)"};

        String[][] bodyDatass = new String[playtimeList.size()][7];
        for (int i = 0; i < playtimeList.size(); i++) {
            IndicatorsResponse.Playtime entry = playtimeList.get(i);
            bodyDatass[i][0] = String.valueOf(entry.getDate());
            bodyDatass[i][1] = String.valueOf(entry.getUserCnt().get(0));
            bodyDatass[i][2] = String.valueOf(entry.getUserCnt().get(1));
            bodyDatass[i][3] = String.valueOf(entry.getUserCnt().get(2));
            bodyDatass[i][4] = String.valueOf(entry.getUserCnt().get(3));
            bodyDatass[i][5] = String.valueOf(entry.getTotalTime());
            bodyDatass[i][6] = String.valueOf(entry.getAverageTime());
        }
        String outfileName = "PlayTime_"+ LocalDateTime.now().getNano();
        // 엑셀 파일 생성 및 다운로드
        try {
            excelUtils.excelDownload(sheetName,headerNames,bodyDatass,outfileName,res);
        }catch (IOException exception){
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.ERROR_EXCEL_DOWN.getMessage());
        }

    }

    public List<IndicatorsResponse.DailyGoods> getDailyGoodsList(String currencyType, List<Currencys> dailyList
                                                            , List<Currencys> totalList){
        List<IndicatorsResponse.DailyGoods> resList = dailyList.stream()
                .collect(Collectors.groupingBy(Currencys::getDate))
                .entrySet().stream()
                .sorted(Map.Entry.comparingByKey())
                .map(entry -> {
                    LocalDate currentDate = entry.getKey();
                    List<Currencys> currentDayList = entry.getValue();

                    List<IndicatorsResponse.Total> totals = totalList.stream()
                            .filter(t -> currentDate.isEqual(t.getDate()))
                            .map(t -> {
                                IndicatorsResponse.Total total = new IndicatorsResponse.Total();
                                total.setDate(currentDate);
                                total.setDeltaType(t.getDeltaType());
                                total.setQuantity(t.getQuantity());

                                // 이전 날짜의 Total 객체를 찾습니다.
                                Currencys previousTotal = totalList.stream()
                                        .filter(previous -> currentDate.minusDays(1).isEqual(previous.getDate()))
                                        .findFirst()
                                        .orElse(null);

                                // 이전 날짜의 Total이 존재할 경우 dif를 계산합니다.
                                if (previousTotal != null) {
                                    double previousQuantity = previousTotal.getQuantity();
                                    double currentQuantity = total.getQuantity();

                                    // dif를 계산하여 설정합니다. (이전 날짜 대비 증가율 계산)
                                    if (previousQuantity != 0) {
                                        double dif = Math.round(((currentQuantity - previousQuantity) / previousQuantity) * 100);
                                        total.setDif(dif+"%");
                                    } else {
                                        // 이전 날짜의 Quantity가 0인 경우 예외 처리 (분모가 0일 때)
                                        total.setDif(String.valueOf(Double.POSITIVE_INFINITY));
                                    }
                                } else {
                                    // 이전 날짜의 Total이 없는 경우 예외 처리
                                    total.setDif("");
                                }
                                return total;
                            })
                            .collect(Collectors.toList());

                    List<ROUTE> acquireRoutes = Arrays.asList(
                            ROUTE.QUEST_REWARD, ROUTE.SEASON_PASS, ROUTE.FROM_PROP,
                            ROUTE.CLAIM_REWARD, ROUTE.USE_ITEM, ROUTE.TATTOO_ENHANCE,
                            ROUTE.TATTOO_CONVERSION, ROUTE.SHOP_BUY, ROUTE.SHOP_SELL,
                            ROUTE.PAID_PRODUCT, ROUTE.TRADE_ADD, ROUTE.NFT_LOCKIN
                    );

                    List<ROUTE> consumeRoutes = Arrays.asList(
                            ROUTE.SHOP_BUY, ROUTE.SHOP_SELL, ROUTE.USE_PROP,
                            ROUTE.USE_TAXI, ROUTE.TATTOO_ENHANCE, ROUTE.TATTOO_CONVERSION,
                            ROUTE.SHOUT, ROUTE.SUMMON, ROUTE.CREATE_PARTYROOM,
                            ROUTE.INVENTORY_EXPAND, ROUTE.TRADE_REMOVE, ROUTE.NFT_UNLOCK
                    );
                    List<IndicatorsResponse.DailyData> dailyDataList = currentDayList.stream()
                            .collect(Collectors.groupingBy(Currencys::getDeltaType))
                            .entrySet().stream()
                            .map(dataEntry -> {
                                String deltaType = dataEntry.getKey();
                                List<Currencys> data = dataEntry.getValue();

                                List<Currencys> modifiedData = new ArrayList<>();

                                if ("ACQUIRE".equals(deltaType)) {
                                    for (ROUTE  route : acquireRoutes) {
                                        boolean containsRoute = data.stream().anyMatch(d -> route.name().equals(d.getRoute()));
                                        if (!containsRoute) {
                                            Currencys additionalData = new Currencys();
                                            additionalData.setRoute(route.getDescription());
                                            additionalData.setQuantity(0);
                                            additionalData.setDate(data.get(0).getDate());
                                            additionalData.setCurrencyType(currencyType);
                                            modifiedData.add(additionalData);
                                        }
                                    }
                                } else if ("CONSUME".equals(deltaType)) {
                                    for (ROUTE route : consumeRoutes) {
                                        boolean containsRoute = data.stream().anyMatch(d -> route.name().equals(d.getRoute()));
                                        if (!containsRoute) {
                                            Currencys additionalData = new Currencys();
                                            additionalData.setRoute(route.getDescription());
                                            additionalData.setQuantity(0);
                                            additionalData.setDate(data.get(0).getDate());
                                            additionalData.setCurrencyType(currencyType);
                                            modifiedData.add(additionalData);
                                        }
                                    }
                                }
                                List<Currencys> lastData = data.stream()
                                        .map(d -> {
                                            ROUTE route = Arrays.stream(ROUTE.values())
                                                    .filter(r -> r.name().equals(d.getRoute()))
                                                    .findFirst()
                                                    .orElse(null);
                                            if (route != null) {
                                                d.setRoute(route.getDescription());
                                            }
                                            return d;
                                        })
                                        .collect(Collectors.toList());
                                modifiedData.addAll(lastData);

                                IndicatorsResponse.DailyData dailyData = new IndicatorsResponse.DailyData();
                                dailyData.setDeltaType(deltaType);
                                dailyData.setDate(data.get(0).getDate());
                                dailyData.setData(modifiedData);
                                return dailyData;
                            })
                            .collect(Collectors.toList());

                    IndicatorsResponse.DailyGoods build = IndicatorsResponse.DailyGoods.builder()
                            .date(currentDate)
                            .total(totals)
                            .dailyData(dailyDataList)
                            .build();
                    return build;
                })
                .collect(Collectors.toList());
        return resList;
    }
    //재화 지표
    public IndicatorsResponse getCurrencyUse(Map<String, String> requestParams) {
        LocalDate startDt = LocalDate.parse(CommonUtils.objectToString(requestParams.get("start_dt")));
        LocalDate endDt = LocalDate.parse(CommonUtils.objectToString(requestParams.get("end_dt")));
        String currencyType = CommonUtils.objectToString(requestParams.get("currency_type"));

        List<Currencys> dailyList = getCurrencyCount(startDt, endDt, currencyType);
        List<Currencys> totalList = getDailyGoodsTotal(startDt, endDt, currencyType);

        List<IndicatorsResponse.DailyGoods> dailyGoodsList = getDailyGoodsList(currencyType, dailyList, totalList);

        return IndicatorsResponse.builder()
                .resultData(IndicatorsResponse.ResultData.builder()
                        .dailyGoods(dailyGoodsList)
                        .build())
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();
    }

    //재화 지표 엑셀 다운로드
    public void currencyExcelDown(Map<String, String> requestParams, HttpServletResponse res){

        LocalDate startDt = LocalDate.parse(CommonUtils.objectToString(requestParams.get("start_dt")));
        LocalDate endDt = LocalDate.parse(CommonUtils.objectToString(requestParams.get("end_dt")));
        String currencyType = CommonUtils.objectToString(requestParams.get("currency_type"));

        List<Currencys> dailyList = getCurrencyCount(startDt, endDt, currencyType);
        List<Currencys> totalList = getDailyGoodsTotal(startDt, endDt, currencyType);

        List<IndicatorsResponse.DailyGoods> dailyGoodsList = getDailyGoodsList(currencyType, dailyList, totalList);

        // 엑셀 파일 생성 및 다운로드
        try {
            ExcelUtils.exportToExcelByDailyGoods(dailyGoodsList, res);
        }catch (IOException exception){
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.ERROR_EXCEL_DOWN.getMessage());
        }

    }

    //VBP 지표
    public IndicatorsResponse getVBPList(Map<String, String> requestParams){
        String startDt = CommonUtils.objectToString(requestParams.get("start_dt"));
        String endDt = CommonUtils.objectToString(requestParams.get("end_dt"));
        List<LocalDate> dateRange = CommonUtils.dateRange(startDt, endDt);
        List<Currencys> dailyGoods = new ArrayList<>();
        for (LocalDate date : dateRange){
            List<Currencys> currencysList = Arrays.asList(
                    Currencys.builder().route("OFFLINE_VISIT").deltaType("GET").date(date).count(0).build(),
                    Currencys.builder().route("OFFLINE_PAYMENT").deltaType("GET").date(date).count(0).build(),
                    Currencys.builder().route("ONLINE_VISIT").deltaType("GET").date(date).count(0).build(),
                    Currencys.builder().route("ONLINE_PAYMENT").deltaType("GET").date(date).count(0).build(),
                    Currencys.builder().route("STORE").deltaType("USE").date(date).count(0).build(),
                    Currencys.builder().route("VBP").deltaType("USE").date(date).count(0).build()
            );
            dailyGoods.addAll(currencysList);
        }

        return IndicatorsResponse.builder()
                .resultData(IndicatorsResponse.ResultData.builder()
                        .currencysList(dailyGoods)
                        .build())
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();

    }
    //이이템 지표
    public IndicatorsResponse getItemList(Map<String, String> requestParams){
        String startDt = CommonUtils.objectToString(requestParams.get("start_dt"));
        String endDt = CommonUtils.objectToString(requestParams.get("end_dt"));
        List<LocalDate> dateRange = CommonUtils.dateRange(startDt, endDt);
        List<Currencys> dailyGoods = new ArrayList<>();
        for (LocalDate date : dateRange){
            List<Currencys> currencysList = Arrays.asList(
                    Currencys.builder().route("Dummy_GET_1").deltaType("GET").date(date).count(0).build(),
                    Currencys.builder().route("Dummy_GET_2").deltaType("GET").date(date).count(0).build(),
                    Currencys.builder().route("Dummy_GET_3").deltaType("GET").date(date).count(0).build(),
                    Currencys.builder().route("Dummy_USE_1").deltaType("USE").date(date).count(0).build(),
                    Currencys.builder().route("Dummy_USE_2").deltaType("USE").date(date).count(0).build(),
                    Currencys.builder().route("Dummy_USE_3").deltaType("USE").date(date).count(0).build()
            );
            dailyGoods.addAll(currencysList);
        }

        return IndicatorsResponse.builder()
                .resultData(IndicatorsResponse.ResultData.builder()
                        .currencysList(dailyGoods)
                        .build())
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();

    }
    //인스턴스 지표
    public IndicatorsResponse getInstanceList(Map<String, String> requestParams){
        String startDt = CommonUtils.objectToString(requestParams.get("start_dt"));
        String endDt = CommonUtils.objectToString(requestParams.get("end_dt"));
        List<LocalDate> dateRange = CommonUtils.dateRange(startDt, endDt);
        List<Currencys> dailyGoods = new ArrayList<>();
        for (LocalDate date : dateRange){
            List<Currencys> currencysList = Arrays.asList(
                    Currencys.builder().route("Dummy_Instance_1").date(date).count(0).build(),
                    Currencys.builder().route("Dummy_Instance_1").date(date).count(0).build()
            );
            dailyGoods.addAll(currencysList);
        }

        return IndicatorsResponse.builder()
                .resultData(IndicatorsResponse.ResultData.builder()
                        .currencysList(dailyGoods)
                        .build())
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();

    }

    //의상/타투 지표
    public IndicatorsResponse getClothesList(Map<String, String> requestParams){
        String startDt = CommonUtils.objectToString(requestParams.get("start_dt"));
        String endDt = CommonUtils.objectToString(requestParams.get("end_dt"));
        List<LocalDate> dateRange = CommonUtils.dateRange(startDt, endDt);
        List<Currencys> dailyGoods = new ArrayList<>();
        for (LocalDate date : dateRange){
            List<Currencys> currencysList = Arrays.asList(
                    Currencys.builder().route("Dummy_Item_Id_1").date(date).count(0).build(),
                    Currencys.builder().route("Dummy_Item_Id_2").date(date).count(0).build()
                    );
            dailyGoods.addAll(currencysList);
        }

        return IndicatorsResponse.builder()
                .resultData(IndicatorsResponse.ResultData.builder()
                        .currencysList(dailyGoods)
                        .build())
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();

    }


    //DAU 지표
    public IndicatorsResponse getDauDataList(Map<String, String> requestParams){
        LocalDate startDt = LocalDate.parse(CommonUtils.objectToString(requestParams.get("start_dt")));
        LocalDate endDt = LocalDate.parse(CommonUtils.objectToString(requestParams.get("end_dt")));

        // List<DailyActiveUser> dailyActiveUserList = getDauList(startDt, endDt);

        // return IndicatorsResponse.builder()
        //         .resultData(IndicatorsResponse.ResultData.builder()
        //                 .dailyActiveUserList(dailyActiveUserList)
        //                 .build())
        //         .status(CommonCode.SUCCESS.getHttpStatus())
        //         .result(CommonCode.SUCCESS.getResult())
        //         .build();
                
        return null;
    }

    public void dauExcelDown(Map<String, String> requestParams, HttpServletResponse res){
        LocalDate startDt = LocalDate.parse(CommonUtils.objectToString(requestParams.get("start_dt")));
        LocalDate endDt = LocalDate.parse(CommonUtils.objectToString(requestParams.get("end_dt")));

        String fileName = CommonUtils.objectToString(requestParams.get("file"));
        //List<DailyActiveUser> dailyActiveUserList = getDauList(startDt, endDt);

        String sheetName = "DAU";

        String headerNames[] = new String[]{"일자","dau","dalc", "dglc", "max_au"
                , "00:00","01:00","02:00","03:00","04:00","05:00"
                , "06:00","07:00","08:00","09:00","10:00","11:00"
                , "12:00","13:00","14:00","15:00","16:00","17:00"
                , "18:00","19:00","20:00","21:00","22:00","23:00"};

        int colLength = headerNames.length;

        // String[][] bodyDatass = new String[dailyActiveUserList.size()][colLength];
        // for (int i = 0; i < dailyActiveUserList.size(); i++) {
            
        //     var entry = dailyActiveUserList.get(i);
            // bodyDatass[i][0] = String.valueOf(entry.getDate());
            // bodyDatass[i][1] = String.valueOf(entry.getDau());
            // bodyDatass[i][2] = String.valueOf(entry.getDalc());
            // bodyDatass[i][3] = String.valueOf(entry.getDglc());
            // bodyDatass[i][4] = String.valueOf(entry.getMaxAu());
            // bodyDatass[i][5] = String.valueOf(entry.getH0());
            // bodyDatass[i][6] = String.valueOf(entry.getH1());
            // bodyDatass[i][7] = String.valueOf(entry.getH2());
            // bodyDatass[i][8] = String.valueOf(entry.getH3());
            // bodyDatass[i][9] = String.valueOf(entry.getH4());
            // bodyDatass[i][10] = String.valueOf(entry.getH5());

            // bodyDatass[i][11] = String.valueOf(entry.getH6());
            // bodyDatass[i][12] = String.valueOf(entry.getH7());
            // bodyDatass[i][13] = String.valueOf(entry.getH8());
            // bodyDatass[i][14] = String.valueOf(entry.getH9());
            // bodyDatass[i][15] = String.valueOf(entry.getH10());
            // bodyDatass[i][16] = String.valueOf(entry.getH11());

            // bodyDatass[i][17] = String.valueOf(entry.getH12());
            // bodyDatass[i][18] = String.valueOf(entry.getH13());
            // bodyDatass[i][19] = String.valueOf(entry.getH14());
            // bodyDatass[i][20] = String.valueOf(entry.getH15());
            // bodyDatass[i][21] = String.valueOf(entry.getH16());
            // bodyDatass[i][22] = String.valueOf(entry.getH17());

            // bodyDatass[i][23] = String.valueOf(entry.getH18());
            // bodyDatass[i][24] = String.valueOf(entry.getH19());
            // bodyDatass[i][25] = String.valueOf(entry.getH20());
            // bodyDatass[i][26] = String.valueOf(entry.getH21());
            // bodyDatass[i][27] = String.valueOf(entry.getH22());
            // bodyDatass[i][28] = String.valueOf(entry.getH23());

            //위 28이란 숫자를 collength 값에 종속적으로 바꾸고 싶은데... 고민해보자
        //}
        
        // 엑셀 파일 생성 및 다운로드
        // try {
        //     excelUtils.excelDownload(sheetName,headerNames,bodyDatass,fileName,res);
        // }catch (IOException exception){
        //     throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.ERROR_EXCEL_DOWN.getMessage());
        // }

    }


    /*
    //Daily Medal 지표
    public IndicatorsResponse getDailyMedalDataList(Map<String, String> requestParams){
        LocalDate startDt = LocalDate.parse(CommonUtils.objectToString(requestParams.get("start_dt")));
        LocalDate endDt = LocalDate.parse(CommonUtils.objectToString(requestParams.get("end_dt")));

        List<DailyMedal> dailyMedal = getDailyMedal(startDt, endDt);

        return IndicatorsResponse.builder()
                .resultData(IndicatorsResponse.ResultData.builder()
                        .dailyMedalList(dailyMedal)
                        .build())
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();
                
    }
    
    

    public List<DailyMedal> getDailyMedal(LocalDate startTime, LocalDate endTime){

        Map map = new HashMap();
        map.put("startDt" , CommonUtils.startOfDay(startTime));
        map.put("endDt" , CommonUtils.endOfDay(endTime));
        List<DailyMedal> dateActive = indicatorsMapper.getDailyMedal(map);

        // 결과를 처리하여 결과값 반환
        return dateActive;
    }
     */

    
    public List<Object> getDauList(LocalDate startTime, LocalDate endTime){

        Map map = new HashMap();
        map.put("startDt" , CommonUtils.startOfDay(startTime));
        map.put("endDt" , CommonUtils.endOfDay(endTime));
        //List<DailyActiveUser> dateActive = indicatorsMapper.getDailyActiveUserList(map);

        // 결과를 처리하여 결과값 반환
        return null;
    }

    
    public List<DauLogInfo> getActiveUserCount(LocalDate startTime, String endTime){

        //Map map = new HashMap();
        //map.put("startDt" , CommonUtils.startOfDay(startTime));
        //map.put("endDt" , CommonUtils.endOfDay(endTime));
        //List<Distinct> dateActive = indicatorsMapper.getDailyActive(map);

        // 결과를 처리하여 결과값 반환
        List<DauLogInfo> ativeUsers = indicatorsAuLoadService.getIndicatorsLogData(endTime, endTime, DauLogInfo.class);

        return ativeUsers;
    }
    public List<Currencys> getCurrencyCount(LocalDate startTime, LocalDate endTime, String type){

        Map inputMap = new HashMap();
        inputMap.put("startDt" , CommonUtils.startOfDay(startTime));
        inputMap.put("endDt" , CommonUtils.endOfDay(endTime));
        inputMap.put("currencyType" , type);
        //List<Currencys> currencysList = indicatorsMapper.getDailyGoods(inputMap);

        // 결과를 처리하여 결과값 반환
        return new ArrayList<Currencys>();
    }
    public List<Currencys> getDailyGoodsTotal(LocalDate startTime, LocalDate endTime, String type){

        Map inputMap = new HashMap();
        inputMap.put("startDt" , CommonUtils.startOfDay(startTime));
        inputMap.put("endDt" , CommonUtils.endOfDay(endTime));
        inputMap.put("currencyType" , type);
        //List<Currencys> total = indicatorsMapper.getDailyGoodsTotal(inputMap);

        // 결과를 처리하여 결과값 반환
        return new ArrayList<Currencys>();
    }

    // 데이터 병합
    private List<IndicatorsResponse.userStatistics> getMergeUserStatisticsData(String startDt, String endDt){
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            List<DauLogInfo> dauList = indicatorsDauLoadService.getIndicatorsLogData(startDt, endDt, DauLogInfo.class);
            List<DglcLogInfo> dglcList = indicatorsDglcLoadService.getIndicatorsLogData(startDt, endDt, DglcLogInfo.class);
            List<McuLogInfo> mcuList = indicatorsMcuLoadService.getIndicatorsLogData(startDt, endDt, McuLogInfo.class);
            List<PlayTimeLogInfo> playtimeList = indicatorsPlayTimeLoadService.getIndicatorsLogData(startDt, endDt, PlayTimeLogInfo.class);
            List<NruLogInfo> nruList = indicatorsNruLoadService.getIndicatorsLogData(startDt, endDt, NruLogInfo.class);
            List<DBCapacityInfo> capacityList = indicatorsCapacityLoadService.getIndicatorsLogData(startDt, endDt, DBCapacityInfo.class);
            List<MauLogInfo> mauList = indicatorsMauLoadService.getIndicatorsLogData(startDt, endDt, MauLogInfo.class);
            List<WauLogInfo> wauList = indicatorsWauLoadService.getIndicatorsLogData(startDt, endDt, WauLogInfo.class);
            List<UgqCreateLogInfo> ugqCreateList = indicatorsUgqCreateLoadService.getIndicatorsLogData(startDt, endDt, UgqCreateLogInfo.class);
            List<MetaverseServerInfo> metaverseServerList = indicatorsMetaverServerLoadService.getIndicatorsLogData(startDt, endDt, MetaverseServerInfo.class);
            log.info("getMergeUserStatisticsData Lists get Completed");

            Map<String, Integer> dauMap = dauList.stream()
                    .collect(Collectors.toMap(DauLogInfo::getLogDay, info -> info.getDau() != null ? info.getDau() : 0, (existing, replacement) -> existing));
            Map<String, Integer> dglcMap = dglcList.stream()
                    .collect(Collectors.toMap(DglcLogInfo::getLogDay, info -> info.getDglc() != null ? info.getDglc() : 0, (existing, replacement) -> existing));
            Map<String, Integer> mcuMap = mcuList.stream()
                    .collect(Collectors.toMap(McuLogInfo::getLogDay, info -> info.getMaxCountUser() != null ? info.getMaxCountUser() : 0, (existing, replacement) -> existing));
            Map<String, Long> playtimeMap = playtimeList.stream()
                    .collect(Collectors.toMap(PlayTimeLogInfo::getLogDay, info -> info.getTotalPlayTimeCount() != null ? info.getTotalPlayTimeCount() : 0L, (existing, replacement) -> existing));
            Map<String, Integer> nruMap = nruList.stream()
                    .collect(Collectors.toMap(NruLogInfo::getLogDay, info -> info.getNru() != null ? info.getNru() : 0, (existing, replacement) -> existing));
            Map<String, DBCapacityInfo> capacityMap = capacityList.stream()
                    .collect(Collectors.toMap(DBCapacityInfo::getLogDay, info -> info, (existing, replacement) -> existing));
            Map<String, Integer> mauMap = mauList.stream()
                    .collect(Collectors.toMap(MauLogInfo::getLogDay, info -> info.getMau() != null ? info.getMau() : 0, (existing, replacement) -> existing));
            Map<String, Integer> wauMap = wauList.stream()
                    .collect(Collectors.toMap(WauLogInfo::getLogDay, info -> info.getWau() != null ? info.getWau() : 0, (existing, replacement) -> existing));
            Map<String, Integer> ugqCreateMap = ugqCreateList.stream()
                    .collect(Collectors.toMap(UgqCreateLogInfo::getLogDay, info -> info.getUgqCrateCount() != null ? info.getUgqCrateCount() : 0, (existing, replacement) -> existing));
            Map<String, Integer> metaverseServerMap = metaverseServerList.stream()
                    .collect(Collectors.toMap(MetaverseServerInfo::getLogDay, info -> info.getServerCount() != null ? info.getServerCount() : 0, (existing, replacement) -> existing));
            log.info("getMergeUserStatisticsData Lists To Map Convert Completed");

            Set<String> allDates = new TreeSet<>();
            allDates.addAll(dauMap.keySet());
            allDates.addAll(dglcMap.keySet());
            allDates.addAll(mcuMap.keySet());
            allDates.addAll(playtimeMap.keySet());
            allDates.addAll(nruMap.keySet());
            allDates.addAll(capacityMap.keySet());
            allDates.addAll(mauMap.keySet());
            allDates.addAll(wauMap.keySet());
            allDates.addAll(ugqCreateMap.keySet());
            allDates.addAll(metaverseServerMap.keySet());
            log.info("getMergeUserStatisticsData Lists Merge Completed");

            return allDates.stream()
                    .map(date -> {
                        DBCapacityInfo capacity = capacityMap.getOrDefault(date, null);

                        return IndicatorsResponse.userStatistics.builder()
                                .date(date)
                                .dau(dauMap.getOrDefault(date, 0))
                                .dglc(dglcMap.getOrDefault(date, 0))
                                .mcu(mcuMap.getOrDefault(date, 0))
                                .playtime(playtimeMap.getOrDefault(date, 0L))
                                .nru(nruMap.getOrDefault(date, 0))
                                .readCapacity(capacity != null ? capacity.getConsumeReadTotal() : 0L)
                                .writeCapacity(capacity != null ? capacity.getConsumeWriteTotal() : 0L)
                                .wau(wauMap.getOrDefault(date, 0))
                                .mau(mauMap.getOrDefault(date, 0))
                                .ugqCreate(ugqCreateMap.getOrDefault(date, 0))
                                .serverCount(metaverseServerMap.getOrDefault(date, 0))
                                .build();
                    })
                    .collect(Collectors.toList());
        }catch (Exception e){
            log.error("Error UserStatisticsData Merge Fail", e);
        }
        return List.of();
    }
}
