package com.bsmlab.dfx.dfxconsole.app.communicate.service;

import com.bsmlab.dfx.agent.config.AgentConfigDto;
import com.bsmlab.dfx.agent.listener.dto.AckDto;
import com.bsmlab.dfx.agent.listener.dto.CommandDto;
import com.bsmlab.dfx.agent.listener.dto.ReceiveMessageDto;
import com.bsmlab.dfx.agent.support.MessageUtils;
import com.bsmlab.dfx.agent.support.exception.IllegalMessageException;
import com.bsmlab.dfx.agent.support.exception.InCompleteMessageException;
import com.bsmlab.dfx.agent.support.exception.NullMessageException;
import com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentInfoDto;
import com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentInfoService;
import com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentMessageHistoryDto;
import com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentMessageHistoryMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;

@Service
@Slf4j
@RequiredArgsConstructor
public class ListenerService {
    private final DfxAgentInfoService dfxAgentInfoService;
    private final DfxAgentMessageHistoryMapper dfxAgentMessageHistoryMapper;

    @Transactional
    public AckDto receiveCommand(String messageJsonString) {
        AckDto ackDto;
        CommandDto commandDto;
        try {
            commandDto = MessageUtils.toCommandDto(messageJsonString);
            String resultText = null;
            if(CommandDto.CommandType.ALIVE == commandDto.getCommandType()) {
                resultText = CommandDto.CommandType.ALIVE.name();
            }
            else if(CommandDto.CommandType.INFORMATION == commandDto.getCommandType()) {
                this.saveAgentInformation(commandDto);
                resultText = CommandDto.CommandType.INFORMATION.name();
            }
            ackDto = AckDto.builder().result(AckDto.ResultType.PROCESS_SUCCESS).messageUuid(commandDto.getMessageUuid()).resultText(resultText).build();
        }
        catch (IllegalMessageException | NullMessageException | JsonProcessingException e) {
            ackDto = AckDto.builder().result(AckDto.ResultType.RECEIVE_FAIL).resultText(e.getLocalizedMessage()).messageUuid("").build();
        }
        return ackDto;
    }

    public void saveAgentInformation(CommandDto commandDto) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        AgentConfigDto agentConfigDto = objectMapper.readValue(commandDto.getData(), new TypeReference<AgentConfigDto>(){});
        if(agentConfigDto != null && StringUtils.isNotEmpty(agentConfigDto.getMyHostId())) {
            DfxAgentInfoDto dfxAgentInfoDto = DfxAgentInfoDto.builder()
                    .agentId(agentConfigDto.getMyHostId()).hostName(agentConfigDto.getMyHostName())
                    .listenPort(agentConfigDto.getMyListenPort()).description(agentConfigDto.getDescription())
                    .postmanCount(agentConfigDto.getPostmanConfig().getPostmanList().size())
                    .dropboxCount(agentConfigDto.getDropBoxConfig().getDropBoxList().size())
                    .settingsData(commandDto.getData()).lastStatusTs(System.currentTimeMillis()).statusCode("STATUS_OK")
                    .build();
            dfxAgentInfoService.saveDfxAgentInfoDto(dfxAgentInfoDto);
        }
    }

    /**
     * 메시지를 파싱한다.
     * 메시지의 recipientHostId가 myHostId 이거나 메시지의 라우팅 중 마지막 hostId가 myHostId 이면 수신처리 한다.
     * 그렇지 않은 경우 라우팅 다음 순번으로 메시지를 전송한다. (MessageUtils.transfer)
     * @param messageJsonString
     * @return
     */
    @Transactional
    public AckDto receiveMessage(String messageJsonString) {
        AckDto ackDto = null;
        try {
            ReceiveMessageDto receiveMessageDto = MessageUtils.toReceiveMessageDto(messageJsonString);
            ObjectMapper objectMapper = new ObjectMapper();
            int messageDataCount = 0;
            try {
                List<Map<String, Object>> dataMapList = objectMapper.readValue(receiveMessageDto.getData(), new TypeReference<List<Map<String, Object>>>() {});
                messageDataCount = dataMapList.size();
            } catch (JsonProcessingException e) {
                log.error("cannot convert data on receiveMessageDto messageUuid: {}", receiveMessageDto.getMessageUuid());
            }
            DfxAgentMessageHistoryDto dfxAgentMessageHistoryDto = DfxAgentMessageHistoryDto.builder()
                    .senderAgentId(receiveMessageDto.getSenderHostId())
                    .senderTs(receiveMessageDto.getSenderTimestamp())
                    .senderPostmanId(receiveMessageDto.getSenderPostmanId())
                    .recipientAgentId(receiveMessageDto.getRecipientHostId())
                    .recipientTs(receiveMessageDto.getReceivedTimestamp())
                    .recipientDropboxId(receiveMessageDto.getRecipientDropBoxId())
                    .messageUuid(receiveMessageDto.getMessageUuid())
                    .messageTypeCode(receiveMessageDto.getMessageType().name())
                    .consoleReceiveTs(System.currentTimeMillis())
                    .processStatusCode(receiveMessageDto.getProcessStatus().name())
                    .messageData(receiveMessageDto.getData()).messageDataCount(messageDataCount)
                    .build();
            dfxAgentMessageHistoryMapper.insertDfxAgentMessageHistory(dfxAgentMessageHistoryDto);
            ackDto = AckDto.builder().result(AckDto.ResultType.RECEIVE_SUCCESS).build();
        } catch (IllegalMessageException | NullMessageException | InCompleteMessageException e) {
            log.error("{}", e, e);
            ackDto = AckDto.builder().result(AckDto.ResultType.RECEIVE_FAIL).resultText(e.getLocalizedMessage()).build();
        }
        return ackDto;
    }
}
