package com.arms.api.serverinfo.service;

import com.arms.api.serverinfo.model.ServerInfo;
import com.arms.api.serverinfo.model.ServerInfoEntity;
import com.arms.api.util.UUIDUtil;
import com.arms.api.util.aes.AES256Decryption;
import com.arms.api.util.aspect.DwrSendAlarm;
import com.arms.api.serverinfo.model.JiraServerPureEntity;
import com.arms.api.issue.almapi.msa_communicate.BackendJiraServer;
import com.arms.api.util.aspect.SlackSendAlarm;
import com.arms.api.util.errors.ErrorCode;

import com.arms.egovframework.javaservice.esframework.repository.common.EsCommonRepositoryWrapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.modelmapper.ModelMapper;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service("serverInfo_서비스")
@AllArgsConstructor
public class ServerInfoServiceImpl implements ServerInfoService {

    private final EsCommonRepositoryWrapper<ServerInfoEntity> esCommonRepositoryWrapper;

    private final ModelMapper modelMapper;

    private final BackendJiraServer backendJiraServer;

    private final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private final AES256Decryption aes256Decryption;

    @Override
    public String getDecryptPasswordOrToken(ServerInfo serverInfo){
        return aes256Decryption.decrypt(serverInfo.getPasswordOrToken());
    }

    @Override
    public ServerInfoEntity saveOrUpdateServerInfo(ServerInfo serverInfo) {

        ServerInfoEntity serverInfoEntity = modelMapper.map(serverInfo, ServerInfoEntity.class);
        ServerInfoEntity result;

        try {
            result = esCommonRepositoryWrapper.save(serverInfoEntity);
        }
        catch (Exception e) {
            log.error(e.getMessage());
            throw new IllegalArgumentException(e.getMessage(), e);
        }

        return result;
    }

    @Override
    public List<ServerInfo> serverInfoListByConnectIds(List<String> connectIds) {

        return connectIds.stream()
                .map(esCommonRepositoryWrapper::findDocById)
                .filter(Objects::nonNull)
                .map(serverInfoEntity -> {
                    ServerInfo serverInfo = modelMapper.map(serverInfoEntity, ServerInfo.class);
                    // Remove the following 2 lines if these fields are required.
                    serverInfo.setUserId("");
                    serverInfo.setPasswordOrToken("");
                    return serverInfo;
                })
                .collect(Collectors.toList());
    }

    @Override
    @Async
    @DwrSendAlarm(messageOnStart = "serverInfo 백업 시작", messageOnEnd = "serverInfo 백업 종료")
    @SlackSendAlarm(messageOnStart = "serverInfo 백업 시작", messageOnEnd = "serverInfo 백업 종료")
    public void serverInfoBackup() {
        String logMessagePrefix = "[ ServerInfoServiceImpl :: serverInfoBackup ]";
        String shortUUID = UUIDUtil.createShortUUID();

        LocalDateTime startTime = LocalDateTime.now();
        log.info("[{}] :: {} :: started at {}", shortUUID, logMessagePrefix, startTime.format(timeFormatter));

        ResponseEntity<List<JiraServerPureEntity>> response = backendJiraServer.getJiraServerMonitor();
        List<JiraServerPureEntity> serverInfoList = response.getBody();

        Optional.ofNullable(serverInfoList)
            .orElseGet(Collections::emptyList)
            .forEach(serverInfo -> {
                ServerInfoEntity serverInfoEntity =
                        Optional.ofNullable(esCommonRepositoryWrapper.findDocById(serverInfo.getC_jira_server_etc()))
                                .orElseGet(ServerInfoEntity::new);
                serverInfoEntity.setUri(serverInfo.getC_jira_server_base_url());
                serverInfoEntity.setType(serverInfo.getC_jira_server_type());
                serverInfoEntity.setUserId(serverInfo.getC_jira_server_connect_id());
                serverInfoEntity.setPasswordOrToken(serverInfo.getC_jira_server_connect_pw());
                serverInfoEntity.setConnectId(serverInfo.getC_jira_server_etc());
                esCommonRepositoryWrapper.save(serverInfoEntity);
            });

        LocalDateTime endTime = LocalDateTime.now();
        Duration elapsed = Duration.between(startTime, endTime);
        log.info("[{}] :: {} :: finished at {} (completed in {} ms)", shortUUID, logMessagePrefix, endTime.format(timeFormatter), elapsed.toMillis());
    }

    @Override
    public void deleteServerInfo(String serverId) {
        esCommonRepositoryWrapper.deleteById(serverId);
    }

    @Override
    public ServerInfo verifyServerInfo(String serverId) {

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

        ServerInfo serverInfo = getServerInfo(serverId);

        if (serverInfo == null) {
            log.error("등록된 서버 정보가 아닙니다.");
            throw new IllegalArgumentException(ErrorCode.SERVER_INFO_ERROR.getErrorMsg());
        }
        else if (serverInfo.getUri() == null || serverInfo.getUri().isEmpty()) {
            throw new IllegalArgumentException(ErrorCode.SERVER_URI_INFO_ERROR.getErrorMsg());
        }
        else if (serverInfo.getUserId() == null || serverInfo.getUserId().isEmpty()) {
            log.error("사용자 아이디 조회에 실패했습니다.");
            throw new IllegalArgumentException(ErrorCode.SERVER_ID_INFO_ERROR.getErrorMsg());
        }
        else {
            String decryptPasswordOrToken = getDecryptPasswordOrToken(serverInfo);
            if (decryptPasswordOrToken == null || decryptPasswordOrToken.isEmpty()) {
                log.info("비밀 번호 및 토큰 정보 조회에 실패했습니다.");
                throw new IllegalArgumentException(ErrorCode.SERVER_PW_OR_API_TOKEN_INFO_ERROR.getErrorMsg());
            }
        }

        return serverInfo;
    }

    private ServerInfo getServerInfo(String serverId) {
        ServerInfoEntity serverInfoEntity = esCommonRepositoryWrapper.findDocById(serverId);
        return modelMapper.map(serverInfoEntity, ServerInfo.class);
    }

    @Override
    public Map<String, String> getServerIdToTypeMap() {
        return esCommonRepositoryWrapper.findAllHits().toDocs().stream()
                .collect(Collectors.toMap(ServerInfoEntity::getConnectId, ServerInfoEntity::getType));
    }


}
