package com.arms.api.almproject.strategy;

import com.arms.api.almproject.model.vo.ProjectVO;
import com.arms.api.serverinfo.model.vo.ServerInfoVO;
import com.arms.api.serverinfo.service.ServerInfoService;
import com.arms.api.util.alm.RedmineApi;
import com.arms.api.util.alm.RedmineUtil;
import com.arms.api.util.errors.ErrorLogUtil;
import com.taskadapter.redmineapi.RedmineException;
import com.taskadapter.redmineapi.RedmineManager;
import com.taskadapter.redmineapi.bean.Project;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
@AllArgsConstructor
public class OnpremiseRedmineProjectStrategy implements ProjectStrategy {

    private final RedmineUtil redmineUtil;

    private final RedmineApi redmineApi;

    private final ServerInfoService serverInfoService;

    @Override
    public ProjectVO getProject(ServerInfoVO serverInfoVO, String projectKeyOrId) {

        RedmineManager redmineManager = redmineUtil.createRedmineOnPremiseCommunicator(serverInfoVO.getUri(), serverInfoService.getDecryptPasswordOrToken(serverInfoVO.getPasswordOrToken()));

        try {
            Project project = redmineManager.getProjectManager().getProjectById(Integer.parseInt(projectKeyOrId));
            return this.convertProjectVO(project, null, serverInfoVO.getUri());
        }
        catch (RedmineException e) {
            String errorMessage = ErrorLogUtil.exceptionLoggingAndReturn(e, this.getClass().getName(),
                    String.format("%s[%s] :: getProject Error 프로젝트[%s] ",
                            serverInfoVO.getType(), serverInfoVO.getUri(), projectKeyOrId));
            throw new IllegalArgumentException(errorMessage);
        }
    }

    @Override
    public List<ProjectVO> getProjectList(ServerInfoVO serverInfoVO) {

        RedmineManager redmineManager = redmineUtil.createRedmineOnPremiseCommunicator(serverInfoVO.getUri(), serverInfoService.getDecryptPasswordOrToken(serverInfoVO.getPasswordOrToken()));

        List<Project> redmineProjectList;
        try {
            redmineProjectList = redmineManager.getProjectManager().getProjects();
        }
        catch (RedmineException e) {
            String errorMessage = ErrorLogUtil.exceptionLoggingAndReturn(e, this.getClass().getName(),
                    String.format("%s[%s] :: getProjectList Error", serverInfoVO.getType(), serverInfoVO.getUri()));
            throw new IllegalArgumentException(errorMessage);
        }

        Map<Integer, Project> projectMap = redmineProjectList.stream()
                .collect(Collectors.toMap(Project::getId, Function.identity()));

        List<ProjectVO> projectVOS = redmineProjectList.stream()
                .filter(Objects::nonNull)
                .map(프로젝트 -> {
                    String 전체_프로젝트명 = 상위_프로젝트명_설정(프로젝트, projectMap);
                    return this.convertProjectVO(프로젝트, 전체_프로젝트명, serverInfoVO.getUri());
                })
                .collect(Collectors.toList());

        return projectVOS;
    }

    public String 상위_프로젝트명_설정(Project project, Map<Integer, Project> projectMap) {
        // 부모 프로젝트 ID 유무 확인
        return Optional.ofNullable(project.getParentId())
                .map(parentId ->
                        // 있다면 재귀함수 호출로 상위 프로젝트의 Name을 가져와서 설정, 상위 프로젝트 구분자를 위한 '&nbsp;-&nbsp;' 처리
                        상위_프로젝트명_설정(projectMap.get(parentId), projectMap) + "&nbsp;-&nbsp;" + project.getName()
                )
                // 부모 프로젝트가 없다면 프로젝트명 설정
                .orElse(project.getName());
    }

    private ProjectVO convertProjectVO(Project project, String projectName, String serverUrl) {
        if (project == null) {
            return null;
        }

        return ProjectVO.builder()
                .id(String.valueOf(project.getId()))
                .key(String.valueOf(project.getId()))
                .name(Optional.ofNullable(projectName).orElse(project.getName()))
                .self(redmineUtil.checkServerInfoPath(serverUrl) +
                        redmineApi.replaceID(
                                redmineApi.getEndpoint().getProject(), String.valueOf(project.getId())
                        )
                ).build();
    }
}