package com.arms.api.report.weekly.service;

import com.arms.api.issue.almapi.model.entity.AlmIssueEntity;
import com.arms.api.report.weekly.model.dto.AssigneeDTO;
import com.arms.api.report.weekly.model.dto.ReportRequestDTO;
import com.arms.api.report.weekly.model.vo.MonthlyTrendVO;
import com.arms.api.report.weekly.model.vo.PerformanceStackedHorizontalBarVO;
import com.arms.api.report.weekly.model.vo.PerformanceStatePieVO;
import com.arms.api.report.weekly.model.vo.PerformanceComparisonVO;
import com.arms.api.report.weekly.model.vo.PerformanceVO;
import com.arms.api.report.weekly.model.vo.MonthlyCountIndex;
import com.arms.api.report.weekly.model.vo.StateMappingInfo;
import com.arms.api.serverinfo.model.enums.ServerType;
import com.arms.api.serverinfo.service.ServerInfoService;
import com.arms.egovframework.javaservice.esframework.esquery.SimpleQuery;
import com.arms.egovframework.javaservice.esframework.esquery.filter.RangeQueryFilter;
import com.arms.egovframework.javaservice.esframework.model.dto.esquery.*;
import com.arms.egovframework.javaservice.esframework.model.dto.request.AggregationRequestDTO;
import com.arms.egovframework.javaservice.esframework.model.dto.request.CompositeRequestDTO;
import com.arms.egovframework.javaservice.esframework.model.vo.DocumentAggregations;
import com.arms.egovframework.javaservice.esframework.model.vo.DocumentBucket;
import com.arms.egovframework.javaservice.esframework.repository.common.EsCommonRepositoryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static com.arms.egovframework.javaservice.esframework.esquery.SimpleQuery.aggregation;
import static com.arms.egovframework.javaservice.esframework.esquery.SimpleQuery.composite;

@Slf4j
@Service
@AllArgsConstructor
public class WeeklyServiceImpl implements WeeklyService {

    private final EsCommonRepositoryWrapper<AlmIssueEntity> esCommonRepositoryWrapper;
    private final StateMappingCacheService stateMappingCacheService;
    private final ServerInfoService serverInfoService;

    /*
    * 담당자 집계 데이터 - isReq > status
    * */
    @Override
    public PerformanceVO getPerformanceData(ReportRequestDTO reportRequestDTO) {

        if (reportRequestDTO.getStartDate() == null || reportRequestDTO.getStartDate().isEmpty()) {
            String firstDayOf2MonthAgo = LocalDate.now()
                    .minusMonths(2)
                    .withDayOfMonth(1)
                    .format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            reportRequestDTO.setStartDate(firstDayOf2MonthAgo);
        }

        List<AssigneeDTO> activeUsers = getActiveUser(reportRequestDTO);
        DocumentAggregations activeUserAggs = getMonthlyAggregationByAssignee(reportRequestDTO, activeUsers);
        List<DocumentBucket> activeUserAggsBuckets = activeUserAggs.deepestList();


        StateMappingInfo stateMappingInfo = getStateMappingInfo();
        Map<String, String> serverIdToTypeMap = getServerIdToTypeMap();

        DocumentAggregations aggregations = getMonthlyAggregationByAssignee(reportRequestDTO, null);
        List<DocumentBucket> personalAggsBuckets = aggregations.deepestList();

        List<String> categoryNames = List.of("열림", "진행중", "해결됨", "닫힘", "기타");
        Set<String> resolvedCategories = Set.of("해결됨", "닫힘");

        // 월별 집계: month → { reqResolved, reqTotal, notReqResolved, notReqTotal }
        // 3개월 키를 미리 초기화 (데이터 없는 월도 0으로 포함)
        Map<String, int[]> monthlyCountMap = new TreeMap<>();
        LocalDate startDate = LocalDate.parse(reportRequestDTO.getStartDate());
        for (int i = 0; i < 3; i++) {
            String monthKey = startDate.plusMonths(i).format(DateTimeFormatter.ofPattern("yyyy-MM"));
            monthlyCountMap.put(monthKey, new int[MonthlyCountIndex.SIZE]);
        }
        // 3개월 합산 Pie: isReq(true/false) → category → count
        Map<String, Integer> reqCategoryCount = new LinkedHashMap<>();
        Map<String, Integer> notReqCategoryCount = new LinkedHashMap<>();
        categoryNames.forEach(name -> {
            reqCategoryCount.put(name, 0);
            notReqCategoryCount.put(name, 0);
        });
        // 3개월 합산 StackedHBar: [해결, 미해결]
        int reqResolved = 0, reqUnresolved = 0;
        int notReqResolved = 0, notReqUnresolved = 0;

        for (DocumentBucket bucket : personalAggsBuckets) {

            Map<String, Object> objectByName = (Map<String, Object>) bucket.objectByName("monthly");
            String monthly = String.valueOf(objectByName.get("monthly"));
            String isReq = String.valueOf(objectByName.get("isReq"));
            String serverId = String.valueOf(objectByName.get("serverId"));
            String project = String.valueOf(objectByName.get("project"));
            String issuetypeId = String.valueOf(objectByName.get("issuetypeId"));
            String statusId = String.valueOf(objectByName.get("statusId"));
            long count = bucket.countByName("monthly");

            String monthKey = monthly.substring(0, 7); // "2026-01-01T..." → "2026-01"

            String serverType = serverIdToTypeMap.get(serverId);
            String almIssueStatusKey = buildAlmIssueStatusKey(serverType, serverId, project, issuetypeId, statusId);

            String categoryName = stateMappingInfo.resolveCategory(almIssueStatusKey);
            boolean isResolved = resolvedCategories.contains(categoryName);
            boolean isReqTrue = "true".equals(isReq);

            int[] monthlyCounts = monthlyCountMap.computeIfAbsent(monthKey, k -> new int[MonthlyCountIndex.SIZE]);
            if (isReqTrue) {
                if (isResolved) monthlyCounts[MonthlyCountIndex.REQ_RESOLVED.index()] += (int) count;
                monthlyCounts[MonthlyCountIndex.REQ_TOTAL.index()] += (int) count;
            } else {
                if (isResolved) monthlyCounts[MonthlyCountIndex.NOT_REQ_RESOLVED.index()] += (int) count;
                monthlyCounts[MonthlyCountIndex.NOT_REQ_TOTAL.index()] += (int) count;
            }

            // Pie 합산
            Map<String, Integer> targetPie = isReqTrue ? reqCategoryCount : notReqCategoryCount;
            targetPie.merge(categoryName, (int) count, Integer::sum);

            // StackedHBar 합산
            if (isReqTrue) {
                if (isResolved) reqResolved += (int) count;
                else reqUnresolved += (int) count;
            } else {
                if (isResolved) notReqResolved += (int) count;
                else notReqUnresolved += (int) count;
            }

        }

        // activeUsers 집계 (activeUserAggsBuckets)
        long activeUsersReqResolved = 0, activeUsersReqUnresolved = 0;
        long activeUsersNotReqResolved = 0, activeUsersNotReqUnresolved = 0;

        for (DocumentBucket bucket : activeUserAggsBuckets) {

            Map<String, Object> objectByName = (Map<String, Object>) bucket.objectByName("monthly");
            String isReq = String.valueOf(objectByName.get("isReq"));
            String serverId = String.valueOf(objectByName.get("serverId"));
            String project = String.valueOf(objectByName.get("project"));
            String issuetypeId = String.valueOf(objectByName.get("issuetypeId"));
            String statusId = String.valueOf(objectByName.get("statusId"));
            long count = bucket.countByName("monthly");

            String serverType = serverIdToTypeMap.get(serverId);
            String almIssueStatusKey = buildAlmIssueStatusKey(serverType, serverId, project, issuetypeId, statusId);

            String categoryName = stateMappingInfo.resolveCategory(almIssueStatusKey);
            boolean isResolved = resolvedCategories.contains(categoryName);
            boolean isReqTrue = "true".equals(isReq);

            if (isReqTrue) {
                if (isResolved) activeUsersReqResolved += count;
                else activeUsersReqUnresolved += count;
            } else {
                if (isResolved) activeUsersNotReqResolved += count;
                else activeUsersNotReqUnresolved += count;
            }
        }

        // 개인 vs 평균 비교
        PerformanceComparisonVO performanceComparison = PerformanceComparisonVO.from(
                reqResolved, reqUnresolved,
                notReqResolved, notReqUnresolved,
                activeUsersReqResolved, activeUsersReqUnresolved,
                activeUsersNotReqResolved, activeUsersNotReqUnresolved,
                activeUsers.size()
        );

        // MonthlyTrendVO 구성
        List<MonthlyTrendVO> monthlyTrendList = monthlyCountMap.entrySet().stream()
                .map(entry -> {
                    int[] counts = entry.getValue();
                    int monthlyReqResolved = counts[MonthlyCountIndex.REQ_RESOLVED.index()];
                    int monthlyReqTotal = counts[MonthlyCountIndex.REQ_TOTAL.index()];
                    int monthlyNotReqResolved = counts[MonthlyCountIndex.NOT_REQ_RESOLVED.index()];
                    int monthlyNotReqTotal = counts[MonthlyCountIndex.NOT_REQ_TOTAL.index()];
                    return MonthlyTrendVO.builder()
                            .month(entry.getKey())
                            .resolvedTotal(monthlyReqResolved + monthlyNotReqResolved)
                            .issueTotal(monthlyReqTotal + monthlyNotReqTotal)
                            .reqResolvedCount(monthlyReqResolved)
                            .notReqResolvedCount(monthlyNotReqResolved)
                            .build();
                })
                .collect(Collectors.toList());

        // StackedHBar 구성
        List<String> hBarLegends = List.of("해결", "미해결");
        PerformanceStackedHorizontalBarVO reqStackHBar = PerformanceStackedHorizontalBarVO.builder()
                .seriesName("요구사항")
                .legends(hBarLegends)
                .data(List.of(reqResolved, reqUnresolved))
                .build();
        PerformanceStackedHorizontalBarVO notReqStackHBar = PerformanceStackedHorizontalBarVO.builder()
                .seriesName("하위 및 연결이슈")
                .legends(hBarLegends)
                .data(List.of(notReqResolved, notReqUnresolved))
                .build();

        // Pie 구성
        PerformanceStatePieVO reqPie = PerformanceStatePieVO.builder()
                .seriesName("요구사항")
                .categories(categoryNames)
                .data(new ArrayList<>(reqCategoryCount.values()))
                .build();
        PerformanceStatePieVO notReqPie = PerformanceStatePieVO.builder()
                .seriesName("하위 및 연결이슈")
                .categories(categoryNames)
                .data(new ArrayList<>(notReqCategoryCount.values()))
                .build();

        return PerformanceVO.builder()
                .monthlyTrendList(monthlyTrendList)
                .reqStackHBar(reqStackHBar)
                .notReqStackHBar(notReqStackHBar)
                .reqPie(reqPie)
                .notReqPie(notReqPie)
                .performanceComparison(performanceComparison)
                .build();
    }




    private List<AssigneeDTO> getActiveUser(ReportRequestDTO reportRequestDTO) {
        DocumentAggregations documentAggregations = esCommonRepositoryWrapper.aggregateRecent(
                SimpleQuery.aggregation(
                                AggregationRequestDTO.builder()
                                        .mainField("jira_server_id")
                                        .mainFieldAlias("jiraServerId")
                                        .addGroup(
                                                SubGroupFieldDTO.builder()
                                                        .subField("assignee.assignee_accountId.keyword")
                                                        .subFieldAlias("accountId")
                                                        .build()
                                        )
                                        .build()
                        )
                        .andTermQueryFilter("pdServiceId", reportRequestDTO.getPdServiceId())
                        .andTermsQueryFilter("pdServiceVersions", reportRequestDTO.getPdServiceVersions())
                        .andExistsQueryFilter("updated")
                        .andExistsQueryFilter("assignee")
                        .andRangeQueryFilter(RangeQueryFilter.of("created").betweenDate("now-3M/d", "now"))
        );

        List<DocumentBucket> docs = documentAggregations.deepestList();

        if (docs.isEmpty()) {
            return Collections.emptyList();
        }

        return docs.stream()
                .filter(Objects::nonNull)
                .map(bucket -> AssigneeDTO.builder()
                            .serverId(bucket.valueByName("jiraServerId"))
                            .accountId(bucket.valueByName("accountId"))
                            .build()).distinct().toList();
    }

    private DocumentAggregations getMonthlyAggregationByAssignee(ReportRequestDTO reportRequestDTO, List<AssigneeDTO> assigneeList) {

        List<String> accountIdList = new ArrayList<>();

        if (ObjectUtils.isEmpty(assigneeList)) {
            accountIdList.add(reportRequestDTO.getAssignee().getAccountId());
        } else {
            accountIdList = assigneeList.stream().map(list -> list.getAccountId()).distinct().toList();
        }

        SimpleQuery<CompositeGroupDTO> compositeGroupDTOSimpleQuery = composite(
                CompositeRequestDTO.builder()
                        .addSource(
                                CompositeSourceDTO.builder()
                                        .field("updated")
                                        .fieldAlias("monthly")
                                        .isAscending(false)
                                        .field(DocFiledType.HistogramMonth)
                                        .build(),
                                CompositeSourceDTO.builder()
                                        .field("isReq")
                                        .fieldAlias("isReq")
                                        .isAscending(false)
                                        .build(),
                                CompositeSourceDTO.builder()
                                        .field("jira_server_id")
                                        .fieldAlias("serverId")
                                        .isAscending(false)
                                        .build(),
                                CompositeSourceDTO.builder()
                                        .field("project.project_key.keyword")
                                        .fieldAlias("project")
                                        .isAscending(false)
                                        .build(),
                                CompositeSourceDTO.builder()
                                        .field("issuetype.issuetype_id.keyword")
                                        .fieldAlias("issuetypeId")
                                        .isAscending(false)
                                        .build(),
                                CompositeSourceDTO.builder()
                                        .field("status.status_id.keyword")
                                        .fieldAlias("statusId")
                                        .isAscending(false)
                                        .build()
                        )
                        .build()
        )
        .andTermsQueryFilter("linkedIssuePdServiceIds", List.of(reportRequestDTO.getPdServiceId()))
        .andTermsQueryFilter("linkedIssuePdServiceVersions", reportRequestDTO.getPdServiceVersions())
        .andRangeQueryFilter(RangeQueryFilter.of("updated")
                .betweenDate(reportRequestDTO.getStartDate(), reportRequestDTO.getEndDate()))
        .andTermsQueryFilter("assignee.assignee_accountId.keyword", accountIdList);

        return esCommonRepositoryWrapper.compositeAggregationRecent(compositeGroupDTOSimpleQuery);
    }

    private StateMappingInfo getStateMappingInfo() {
        return stateMappingCacheService.getStateMappingInfo();
    }

    private Map<String, String> getServerIdToTypeMap() {
        return serverInfoService.getServerIdToTypeMap();
    }

    private String buildAlmIssueStatusKey(String serverType, String serverId, String project, String issuetypeId, String statusId) {
        if (ObjectUtils.nullSafeEquals(ServerType.CLOUD.getType(), serverType)) {
            return serverId + "-" + project + "-" + issuetypeId + "-" + statusId;
        }
        return serverId + "-" + statusId;
    }
}


