package quant.rich.emoney.controller.api;

import java.io.Serializable;
import java.util.Objects;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.protobuf.nano.MessageNano;

import org.apache.commons.lang3.StringUtils;
import lombok.extern.slf4j.Slf4j;
import nano.BaseResponse.Base_Response;
import quant.rich.emoney.entity.sqlite.ProtocolMatch;
import quant.rich.emoney.exception.RException;
import quant.rich.emoney.pojo.dto.EmoneyConvertResult;
import quant.rich.emoney.pojo.dto.EmoneyProtobufBody;
import quant.rich.emoney.service.RequestResponseInspectService;
import quant.rich.emoney.service.sqlite.ProtocolMatchService;

/**
 * 益盟 ProtocolBuf 报文解析 API 控制器
 */
@RestController
@RequestMapping("/api/v1/proto")
@Slf4j
public class ProtoDecodeControllerV1 {
    
    @Autowired
    ProtocolMatchService protocolMatchService;
    
    @Autowired
    RequestResponseInspectService requestResponseInspectService;
    
    @Autowired
    ObjectMapper objectMapper;
    
    /**
     * 解析 emoney protobuf 的请求
     * @param <U>
     * @param body
     * @return
     */
    @SuppressWarnings("unchecked")
    @PostMapping("/request/decode")
    public <U extends MessageNano> EmoneyConvertResult requestDecode(
            @RequestBody(required=true)
            @NonNull
            EmoneyProtobufBody body) {
        
        Integer protocolId = body.getProtocolId();
        if (Objects.isNull(protocolId)) {
            throw RException.badRequest("protocolId 不能为 null");
        }
        
        ProtocolMatch match = protocolMatchService.getById(protocolId);
        
        if (Objects.isNull(match) || StringUtils.isBlank(match.getClassName())) {
            throw RException
                .badRequest("暂无对应 protocolId = ", protocolId, " 的记录，可等待 response decoder 收集到后再重试");
        }
        
        String className = new StringBuilder()
                .append("nano.")
                .append(match.getClassName())
                .append("Request$")
                .append(match.getClassName())
                .append("_Request")
                .toString();
        // IndexInflow -> nano.IndexInflowRequest$IndexInflow_Request
        
        Class<U> clazz;
        try {
            clazz = (Class<U>)Class.forName(className);
        }
        catch (Exception e) {
            String msg = new StringBuilder()
                    .append("无法根据给定的 protocolId = ")
                    .append(protocolId)
                    .append(", className = ")
                    .append(className)
                    .append("找到对应类").toString();
            log.warn(msg, e);
            throw RException.internalServerError(msg);
        }

        byte[] buf;
        try {
            buf = body.protocolBodyToByte();
        }
        catch (Exception e) {
            throw RException.badRequest("转换 protocolBody 错误");
        }
        
        try {
            U nano = (U)MessageNano.mergeFrom((MessageNano)
                    clazz.getDeclaredConstructor().newInstance(), buf);
            return EmoneyConvertResult
                    .ok(objectMapper.valueToTree(nano))
                    .setProtocolId(protocolId)
                    .setSupposedClassName(className);
        }
        catch (Exception e) {
            String msg = new StringBuilder()
                    .append("转换为类 ")
                    .append(className)
                    .append(" 时错误").toString();
            log.warn(msg, e);
            return EmoneyConvertResult.error(msg)
                    .setProtocolId(protocolId)
                    .setSupposedClassName(className);
        }
        
    }
    
    @SuppressWarnings("unchecked")
    @PostMapping("/response/decode")
    public <U extends MessageNano> EmoneyConvertResult responseDecode(
            @RequestBody(required=false)
            @NonNull
            EmoneyProtobufBody body) {
        
        Integer protocolId = body.getProtocolId();
        ProtocolMatch match = null;
        if (Objects.isNull(protocolId)) {
            log.warn("protocolId 为空 null, 无法更新 protocolMatch");
        }
        else {
            match = protocolMatchService.getById(protocolId);
            if (Objects.isNull(match)) {
                match = new ProtocolMatch().setProtocolId(protocolId);
            }
        }
        
        byte[] buf;
        try {
            buf = body.protocolBodyToByte();
        }
        catch (Exception e) {
            throw RException.badRequest("转换 protocolBody 错误");
        }
        
        Base_Response baseResponse;
        try {
            baseResponse = Base_Response.parseFrom(buf);
        }
        catch (Exception e) {
            String msg = new StringBuilder()
                    .append("转换 BaseResponse 发生错误")
                    .toString();
            log.warn(msg, e);
            return EmoneyConvertResult
                        .error(msg)
                        .setProtocolId(protocolId);
        }
        
        Class<U> clazz;
        String className = 
                baseResponse.detail.getTypeUrl()
                    .replace("type.googleapis.com/", "");

        String rawClassName = className.substring(0, className.lastIndexOf('_'));
        if (Objects.nonNull(match) && StringUtils.isBlank(match.getClassName())) {
            match.setClassName(rawClassName);
            protocolMatchService.saveOrUpdate(match);
        }
        
        className = new StringBuilder()
                .append("nano.")
                .append(rawClassName)
                .append("Response$")
                .append(className)
                .toString();
        
        try {
            clazz = (Class<U>)Class.forName(className);
        }
        catch (Exception e) {
            String msg = new StringBuilder()
                    .append("无法根据给定的 protocolId = ")
                    .append(protocolId)
                    .append(", className = ")
                    .append(className)
                    .append("找到对应类").toString();
            log.warn(msg, e);
            throw RException.internalServerError(msg);
        }
        
        try {
            U nano = (U)MessageNano.mergeFrom(
                    (MessageNano)clazz.getDeclaredConstructor().newInstance(),
                    baseResponse.detail.getValue());
            
            JsonNode jo = objectMapper.valueToTree(nano);
            
            try {
                requestResponseInspectService.inspectResponse(protocolId.toString(), nano, false);
            }
            catch (Exception e) {
                log.warn("执行 Inspect response 时发生错误, protocolId={}", protocolId, e);
            }
            
            return EmoneyConvertResult
                    .ok((Serializable)jo)
                    .setProtocolId(protocolId)
                    .setSupposedClassName(className);
        }
        catch (Exception e) {
            String msg = new StringBuilder()
                    .append("转换为类 ")
                    .append(className)
                    .append(" 时错误").toString();
            log.warn(msg, e);
            return EmoneyConvertResult.error(msg)
                    .setProtocolId(protocolId)
                    .setSupposedClassName(className);
        }
        
    }
    

}
