package net.northking.cctp.element.internal.protocol.socket.cups8583;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import net.northking.cctp.element.core.APIFactory;
import net.northking.cctp.element.core.exception.ComponentRuntimeException;
import net.northking.cctp.element.internal.dto.ColumnDTO;
import net.northking.cctp.element.internal.dto.FieldDto;
import net.northking.cctp.element.internal.protocol.socket.MessageHandler;
import net.northking.cctp.element.internal.protocol.socket.SocketResponseData;
import net.northking.cctp.element.internal.protocol.socket.cups8583.internal.*;
import net.northking.cctp.element.internal.utils.BinaryUtils;
import net.northking.cctp.element.log.Logger;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.FutureTask;

/**
 * CupsMsgProtocol
 *
 * @author fengxiang.zhang
 * @since 1.0
 */
public class CupsMessageHandler implements MessageHandler {

    private static final String NAME = "银联银行卡交换CUPS报文协议";
    private Logger logger = APIFactory.getLogger();

    /**
     * 域定义
     */
    private Map<String, CupsField> defMap;
    /**
     * 字符集
     */
    private Charset charset;

    private String header;

    public CupsMessageHandler(String header,String charsetName) {
        this.defMap = CsvCupsFieldDefReader.defMap;
        this.charset = Charset.forName(charsetName);
        this.header = header;
    }

    @Override
    public String getProtocolName() {
        return NAME;
    }

    @Override
    public byte[] getRequestMessage(String text) throws Exception {
        CupsRequestParam param = JSON.parseObject(text, CupsRequestParam.class);
        if(param.getColumnList()!=null && param.getColumnList().size()>0){
            Map<String, ValueObject> valueMap = new HashMap<>();
            List<ColumnDTO> columnList = param.getColumnList();
            for (int i = 0; i < columnList.size(); i++) {
                ColumnDTO columnDTO =  columnList.get(i);
                ValueObject valueObject = new ValueObject();
                valueObject.setKey(columnDTO.getDomain());
                valueObject.setValue(columnDTO.getDataValue());
                valueObject.setHex(Boolean.valueOf(Integer.toString(columnDTO.getDataValueType())));
                valueMap.put(valueObject.getKey(),valueObject);
            }
            param.setValueMap(valueMap);
        }
        CupsRequest request = CupsRequest.withParam(param);
        logger.debug("############################ Q/CUP 006.2-2018 ############################");

        // 消息类型部分
        byte[] msgTypePart = request.messageType2Bytes();
        logger.debug("--> 请求消息类型:{}, Hex:{}", request.getMessageType(), CupsUtils.bytes2HexString(msgTypePart));

        // 位图报文域部分
        byte[] bodyPart = new CupsMsgBodyEncoder(this.defMap, this.charset).encode(request.getBody());

        // 请求头部分
        byte[] headerPart = request.addMessageLength(msgTypePart.length + bodyPart.length).header2Bytes();
        logger.debug("--> 请求头Hex:{}", CupsUtils.bytes2HexString(headerPart));

        int tradeCodePart = 0;
        byte[] tradePart = null;
        if(StringUtils.isNotBlank(this.header)){
            //拼接请求头
            StringBuilder str = new StringBuilder("");
            List<FieldDto> fieldDtos = JSON.parseArray(this.header, FieldDto.class);
            BinaryUtils.setHeaderLength(fieldDtos,str);
            tradePart = str.toString().getBytes(this.charset);
            tradeCodePart = tradePart.length;
        }
        logger.debug("--> 请求交易码拼接:{}, Hex:{}",this.header,CupsUtils.bytes2HexString(tradePart));

        // 组装全部报文
        ByteBuffer buffer = ByteBuffer.allocate(tradeCodePart+headerPart.length + msgTypePart.length + bodyPart.length);
        if(tradeCodePart>0){
            buffer.put(tradePart);
        }
        buffer.put(headerPart).put(msgTypePart).put(bodyPart);
        return buffer.array();
    }

    @Override
    public FutureTask<SocketResponseData> getResponseFuture(InputStream in) throws Exception {
        // 解码响应头
        byte[] headerBytes = new byte[46];
        readFullBytes(in, headerBytes);
        logger.debug("<-- 响应消息头Hex:{}", CupsUtils.bytes2HexString(headerBytes));
        try {
            CupsHeader cupsHeader = CupsHeader.form(headerBytes);
            if ("00000".equals(cupsHeader.getRejectCode())) {

                // 解码消息类型
                byte[] messageTypeBytes = new byte[4];
                readFullBytes(in, messageTypeBytes);
                logger.debug("<-- 响应消息类型:{}, Hex:{}", new String(messageTypeBytes), CupsUtils.bytes2HexString(messageTypeBytes));

                // 解码位图和域字段
                int remaining = Integer.parseInt(cupsHeader.getTotalMessageLength()) - 46 - 4;
                byte[] bodyBytes = new byte[remaining];
                readFullBytes(in, bodyBytes);
                CupsMsgBodyDecoder decoder = new CupsMsgBodyDecoder(this.defMap, this.charset);
                Object bodyPart = decoder.decode(bodyBytes);

                // 返回结果
                JSONObject result = new JSONObject(true);
                result.put("header", cupsHeader);
                result.put("mti", new String(messageTypeBytes));
                String jsonString = JSON.toJSONString(bodyPart);
                Map<String,Object> parseObject = JSON.parseObject(jsonString, Map.class);
                List<Object> list = new ArrayList<>();
                for (Map.Entry<String,Object> item : parseObject.entrySet()) {
                    Object columnDTO = item.getValue();
                    list.add(columnDTO);
                }
                result.put("columnList", list);
                return new FutureTask(() -> new SocketResponseData().body(result.toJSONString()));
            } else {
                // 请求错误时，返回原始请求报文
                int remaining = Integer.parseInt(cupsHeader.getTotalMessageLength()) - 46;
                byte[] requestBytes = new byte[remaining];
                readFullBytes(in, requestBytes);

                Map<String, Object> result = new HashMap<>(3);
                result.put("header", cupsHeader);
                result.put("context", CupsUtils.bytes2HexString(requestBytes));
                return new FutureTask(() -> new SocketResponseData().body(JSON.toJSONString(result)));
            }
        } catch (Exception e) {
            throw new ComponentRuntimeException("响应报文Q/CUP 006.2-2018解析失败", e);
        }
    }

    /**
     * 读满Byte[]
     */
    private static void readFullBytes(InputStream in, byte[] remainingPart) throws IOException {
        IOUtils.readFully(in, remainingPart);
    }

}
