package com.arms.api.issue.almapi.service;

import com.arms.api.issue.almapi.model.dto.AlmIssueIncrementDTO;
import com.arms.api.issue.almapi.model.dto.AlmIssueWithRequirementDTO;
import com.arms.api.issue.almapi.model.vo.AlmIssueWithRequirementVO;
import com.arms.api.issue.almapi.model.dto.CReqProperty;
import com.arms.api.issue.almapi.model.entity.AlmIssueEntity;
import com.arms.api.issue.almapi.model.vo.AlmIssueVO;
import com.arms.api.issue.almapi.model.dto.AlmIssueDTO;
import com.arms.api.issue.almapi.strategy.*;
import com.arms.api.serverinfo.model.vo.ServerInfoVO;
import com.arms.api.serverinfo.model.enums.ServerType;
import com.arms.api.serverinfo.service.ServerInfoService;
import com.arms.api.util.errors.ErrorCode;
import com.arms.egovframework.javaservice.esframework.esquery.filter.RangeQueryFilter;
import com.arms.egovframework.javaservice.esframework.model.dto.request.AggregationRequestDTO;
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 jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.*;

import static com.arms.api.issue.almapi.model.entity.AlmIssueEntity.CReqProperty.*;
import static com.arms.egovframework.javaservice.esframework.esquery.SimpleQuery.aggregation;

@Slf4j
@Service
@RequiredArgsConstructor
public class IssueService  {

    private final EsCommonRepositoryWrapper<AlmIssueEntity> esCommonRepositoryWrapper;

    private final ServerInfoService serverInfoService;

    private final Map<String, IssueStrategy> strategies;

    public AlmIssueVO createIssue(AlmIssueDTO almIssueDTO) {

        IssueStrategy issueStrategy = this.getALmIssueType(almIssueDTO.getServerId());

        return issueStrategy.createIssue(almIssueDTO);

    }

    public Map<String,Object> updateIssue(AlmIssueDTO almIssueDTO) {

        if (almIssueDTO == null) {
            log.error("이슈 수정하기 Error 지라이슈생성_데이터가 없습니다.");
            throw new IllegalArgumentException("이슈 수정하기 Error 지라이슈생성_데이터가 없습니다.");
        }

        if (almIssueDTO.getServerId() == null) {
            log.error("이슈 수정하기 Error: 연결_아이디 {}", ErrorCode.PARAMETER_SERVER_ID_MISSING.getErrorMsg());
            throw new IllegalArgumentException("이슈 수정하기 Error: 연결_아이디 " + ErrorCode.PARAMETER_SERVER_ID_MISSING.getErrorMsg());
        }

        if (almIssueDTO.getIssueKeyOrId()==null || almIssueDTO.getIssueKeyOrId().isEmpty()) {
            log.error("이슈 수정하기 Error 이슈 키 또는 아이디가 없습니다.");
            throw new IllegalArgumentException(ErrorCode.PARAMETER_NULL_ERROR.getErrorMsg() + " :: 이슈 수정하기 Error 이슈 키 또는 아이디가 없습니다.");
        }


        IssueStrategy issueStrategy = this.getALmIssueType(almIssueDTO.getServerId());

        return issueStrategy.updateIssue(almIssueDTO);

    }

    public Map<String,Object> updateIssueStatus(AlmIssueDTO almIssueDTO) {

        if (almIssueDTO.getServerId() == null) {
            log.error("이슈 상태 변경하기 Error: 연결_아이디 {}" , ErrorCode.PARAMETER_SERVER_ID_MISSING.getErrorMsg());
            throw new IllegalArgumentException("이슈 상태 변경하기 Error: 연결_아이디 " + ErrorCode.PARAMETER_SERVER_ID_MISSING.getErrorMsg());
        }

        if (almIssueDTO.getIssueKeyOrId() == null || almIssueDTO.getIssueKeyOrId().isEmpty()) {
            log.error("이슈 상태 변경하기 Error 이슈 키 또는 아이디가 없습니다.");
            throw new IllegalArgumentException(ErrorCode.PARAMETER_NULL_ERROR.getErrorMsg() + " :: 이슈 상태 변경하기 Error 이슈 키 또는 아이디가 없습니다.");
        }

        if (almIssueDTO.getStatusId() == null || almIssueDTO.getStatusId().isEmpty()) {
            log.error("이슈 상태 변경하기 Error 상태 아이디가 없습니다.");
            throw new IllegalArgumentException("이슈 상태 변경하기 Error 상태 아이디가 없습니다.");
        }

        IssueStrategy issueStrategy = this.getALmIssueType(almIssueDTO.getServerId());

        return issueStrategy.updateIssueStatus(almIssueDTO);

    }

    public Map<String,Object> deleteIssue(AlmIssueDTO almIssueDTO) {

        if (almIssueDTO.getServerId() == null) {
            log.error("이슈 삭제 라벨 처리하기 Error: 연결_아이디 {}" , ErrorCode.PARAMETER_SERVER_ID_MISSING.getErrorMsg());
            throw new IllegalArgumentException("이슈 삭제 라벨 처리하기 Error: 연결_아이디 " + ErrorCode.PARAMETER_SERVER_ID_MISSING.getErrorMsg());
        }

        if (almIssueDTO.getIssueKeyOrId() == null || almIssueDTO.getIssueKeyOrId().isEmpty()) {
            log.error("이슈 삭제 라벨 처리하기 Error 이슈 키 또는 아이디 {}" , ErrorCode.PARAMETER_NULL_ERROR.getErrorMsg());
            throw new IllegalArgumentException("이슈 삭제 라벨 처리하기 Error 이슈 키 또는 아이디 " + ErrorCode.PARAMETER_NULL_ERROR.getErrorMsg());
        }

        IssueStrategy issueStrategy = this.getALmIssueType(almIssueDTO.getServerId());

        return issueStrategy.deleteIssue(almIssueDTO);

    }

    public AlmIssueVO getIssue(AlmIssueDTO almIssueDTO) {

        if (almIssueDTO.getServerId() == null) {
            log.error("이슈 상세정보 가져오기 Error: 연결_아이디 {}" , ErrorCode.PARAMETER_SERVER_ID_MISSING.getErrorMsg());
            throw new IllegalArgumentException("이슈 상세정보 가져오기 Error: 연결_아이디 " + ErrorCode.PARAMETER_SERVER_ID_MISSING.getErrorMsg());
        }

        if (almIssueDTO.getIssueKeyOrId() == null || almIssueDTO.getIssueKeyOrId().isEmpty()) {
            log.error("이슈 상세정보 가져오기 Error 이슈 키 또는 아이디 {}" , ErrorCode.PARAMETER_NULL_ERROR.getErrorMsg());
            throw new IllegalArgumentException("이슈 상세정보 가져오기 Error 이슈 키 또는 아이디 " + ErrorCode.PARAMETER_NULL_ERROR.getErrorMsg());
        }

        IssueStrategy issueStrategy = this.getALmIssueType(almIssueDTO.getServerId());

        return issueStrategy.getIssueVO(almIssueDTO);

    }
    public void cloudJiraTestApiRequest()  {
        strategies.get(String.valueOf(ServerType.CLOUD))
            .cloudJiraTestApiRequest();
    }

    public List<AlmIssueEntity> discoveryIncrementALmIssueAndGetReqAlmIssueEntities(@Valid AlmIssueIncrementDTO almIssueIncrementDTO)  {

        return this.getALmIssueType(almIssueIncrementDTO.getServerId())
                .discoveryIssueAndGetReqEntities(almIssueIncrementDTO);
    }

    public void deleteIfDoesNotExistDoc(){

        DocumentAggregations documentAggregations = this.aggregateByRecentIdBefore30Days();

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

        List<String> deleteList = documentBuckets.stream().map(documentBucket -> {
            String recentId = documentBucket.valueByName("recent_id");

            String connectId = recentId.split("_")[0];
            String key = recentId.split("_")[2];

            IssueStrategy issueStrategy = this.getALmIssueType(connectId);
            AlmIssueDTO almIssueDTO = new AlmIssueDTO();
            almIssueDTO.setServerId(connectId);
            almIssueDTO.setIssueKeyOrId(key);

            try {
                boolean existIssue = issueStrategy.isExistIssue(almIssueDTO);

                if (!existIssue) {
                    return recentId;
                }

                return null;

            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }).filter(Objects::nonNull).toList();

        this.deleteEntities(deleteList);

    }

    private void deleteEntities(List<String> recentIds) {
        recentIds.forEach(recentId->{
            AlmIssueEntity almIssueEntity = esCommonRepositoryWrapper.findRecentDocByRecentId(recentId);
            esCommonRepositoryWrapper.deleteRecentDocById(almIssueEntity.getId());
        });

    }

    private DocumentAggregations aggregateByRecentIdBefore30Days(){

        return esCommonRepositoryWrapper.aggregateRecent(
                aggregation(
                        AggregationRequestDTO.builder()
                                .mainFieldAlias("recent_id")
                                .mainField("recent_id")
                                .build()
                ).andRangeQueryFilter(
                        RangeQueryFilter.of("updated")
                                .gte("now-2M/M")
                                .lt("now-1M/M")
                )
        );
    }

    public void preSaveReqStatus(AlmIssueWithRequirementDTO almIssueWithRequirementDTO) {

        AlmIssueEntity almIssueEntity = esCommonRepositoryWrapper.findRecentDocByRecentId(almIssueWithRequirementDTO.recentId());

        CReqProperty cReqProperty = almIssueWithRequirementDTO.getCReqProperty();

        if(almIssueEntity.getRecentId()==null){
            String title = ObjectUtils.isEmpty(almIssueEntity.getCreated())?"[미확인 이슈]"+ almIssueWithRequirementDTO.getCTitle(): almIssueWithRequirementDTO.getCTitle();
            this.addEntitiesByReqStatus(
                List.of(
                    AlmIssueEntity.builder()
                        .summary(title)
                        .cReqStatusId(almIssueWithRequirementDTO.getCReqStatusId())
                        .recentId(almIssueWithRequirementDTO.recentId())
                        .key(almIssueWithRequirementDTO.getIssueKey())
                        .jira_server_id(almIssueWithRequirementDTO.getJiraServerId())
                        .pdServiceId(almIssueWithRequirementDTO.getServiceId())
                        .cReqLink(almIssueWithRequirementDTO.getCReqLink())
                        .pdServiceVersions(almIssueWithRequirementDTO.getVersions())
                        .project(AlmIssueEntity.Project.builder().key(almIssueWithRequirementDTO.getProjectKeyOrId()).build())
                        .cReqProperty(
                            builder()
                                .cReqPriorityLink(cReqProperty.getCReqPriorityLink())
                                .cReqPriorityName(cReqProperty.getCReqPriorityName())
                                .cReqDifficultyLink(cReqProperty.getCReqDifficultyLink())
                                .cReqDifficultyName(cReqProperty.getCReqDifficultyName())
                                .cReqStateLink(cReqProperty.getCReqStateLink())
                                .cReqStateName(cReqProperty.getCReqStateName())
                            .build()
                        )
                        .isReq(true)
                    .build()
                )
            );
        }

        if(almIssueEntity.getRecentId()!=null&&almIssueEntity.izReqTrue()){
            String title = ObjectUtils.isEmpty(almIssueEntity.getCreated())?"[미확인 이슈]"+ almIssueWithRequirementDTO.getCTitle(): almIssueWithRequirementDTO.getCTitle();
            almIssueEntity.setSummary(title);
            almIssueEntity.setCReqStatusId(almIssueWithRequirementDTO.getCReqStatusId());
            almIssueEntity.setKey(almIssueWithRequirementDTO.getIssueKey());
            almIssueEntity.setJira_server_id(almIssueWithRequirementDTO.getJiraServerId());
            almIssueEntity.setPdServiceId(almIssueWithRequirementDTO.getServiceId());
            almIssueEntity.setCReqLink(almIssueWithRequirementDTO.getCReqLink());
            almIssueEntity.setPdServiceVersions(almIssueWithRequirementDTO.getVersions());
            almIssueEntity.setProject(AlmIssueEntity.Project.builder().key(almIssueWithRequirementDTO.getProjectKeyOrId()).build());
            almIssueEntity.setCReqProperty(
                builder()
                    .cReqPriorityLink(cReqProperty.getCReqPriorityLink())
                    .cReqPriorityName(cReqProperty.getCReqPriorityName())
                    .cReqDifficultyLink(cReqProperty.getCReqDifficultyLink())
                    .cReqDifficultyName(cReqProperty.getCReqDifficultyName())
                    .cReqStateLink(cReqProperty.getCReqStateLink())
                    .cReqStateName(cReqProperty.getCReqStateName())
                .build()
            );

            this.addEntitiesByReqStatus(List.of(almIssueEntity));
        }

    }

    private void addEntitiesByReqStatus(List<AlmIssueEntity> almIssueList) {
        almIssueList.forEach(esCommonRepositoryWrapper::save);
    }

    public AlmIssueEntity findIssueByRecentId(String recentId) {
        return esCommonRepositoryWrapper.findRecentDocByRecentId(recentId);
    }

    private IssueStrategy getALmIssueType(String serverId) {

        ServerInfoVO serverInfoVO = serverInfoService.verifyServerInfo(serverId);

        if (serverInfoVO == null || ObjectUtils.isEmpty(serverInfoVO.getType())) {
            log.error("지라이슈 전략 등록 Error: serverInfo_유형 {}", ErrorCode.SERVER_TYPE_INFO_ERROR.getErrorMsg());
            throw new IllegalArgumentException("이슈 전략 등록 Error: serverInfo_유형 " + ErrorCode.SERVER_TYPE_INFO_ERROR.getErrorMsg());
        }

        String key = ServerType.typeValueOf(serverInfoVO.getType()).name();

        IssueStrategy strategy = strategies.get(key);

        if (strategy == null) {
            throw new IllegalArgumentException("이슈 전략 확인 Error: 허용하지 않는 serverInfo_유형입니다. :: "+ serverInfoVO + " :: " + ErrorCode.SERVER_TYPE_INFO_ERROR.getErrorMsg());
        }

        return strategy;
    }

}
