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

import com.alibaba.fastjson.annotation.JSONField;
import net.northking.cctp.element.core.APIFactory;
import net.northking.cctp.element.core.exception.ComponentRuntimeException;
import net.northking.cctp.element.internal.protocol.socket.cups8583.CupsUtils;
import net.northking.cctp.element.log.Logger;
import org.apache.commons.lang3.ArrayUtils;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;

/**
 * 中国银联银行卡交换系统报文头，报文头总长度46byte
 *
 * @author fengxiang.zhang
 * @since 1.0
 */
public class CupsHeader {

    public static final int HEADER_TOTAL_BYTE_NUM = 46;
    public static final byte HEADER_TOTAL_BYTE_NUM_0 = "0".getBytes()[0];
    /**
     * 组件日志
     */
    private static Logger logger = APIFactory.getLogger();
    /**
     * 1、报文头长度：1Byte，b8
     */
    @JSONField(ordinal = 1)
    private int headerLength;
    /**
     * 2、头标识和版本：1Byte，b8
     */
    @JSONField(ordinal = 2)
    private String headerFlagAndVersion;
    /**
     * 3、整个报文长度：4Byte，n4
     */
    @JSONField(ordinal = 3)
    private String totalMessageLength = String.valueOf(HEADER_TOTAL_BYTE_NUM);
    /**
     * 4、目的ID：11Byte，ans11 不足11位后补空格
     */
    @JSONField(ordinal = 4)
    private String destinationId;
    /**
     * 5、源ID：11Byte，ans11 不足11位后补空格
     */
    @JSONField(ordinal = 5)
    private String sourceId;
    /**
     * 6、CUPS产生，保留使用：3Byte，b24
     */
    @JSONField(ordinal = 6)
    private String reservedForUse;
    /**
     * 7、CUPS产生，批次号：1Byte，b8
     */
    @JSONField(ordinal = 7)
    private String batchNumber;
    /**
     * 8、CUPS产生，交易信息：8Byte，ans8
     */
    @JSONField(ordinal = 8)
    private String transactionInfo;
    /**
     * 9、用户信息：1Byte，b8
     */
    @JSONField(ordinal = 9)
    private String userInfo;
    /**
     * 10、CUPS产生，拒绝码：5Byte，n5
     */
    @JSONField(ordinal = 10)
    private String rejectCode;

    /**
     * 将报文头转换位Byte数组
     */
    public byte[] toBytes() {
        ByteBuffer buffer = ByteBuffer.allocate(HEADER_TOTAL_BYTE_NUM);

        // F1
        byte f1 = this.headerLength != 0 ? Byte.parseByte(String.valueOf(HEADER_TOTAL_BYTE_NUM)) : HEADER_TOTAL_BYTE_NUM_0;
        buffer.put(f1);

        // F2
        if (this.headerFlagAndVersion == null || this.headerFlagAndVersion.length() != 8
                || !CupsUtils.isBinaryStr(this.headerFlagAndVersion)) {
            throw new ComponentRuntimeException("头标识和版本需8位二进制数");
        }
        byte f2 = CupsUtils.binaryStr2Byte(this.headerFlagAndVersion);
        buffer.put(f2);

        // F3
        if (this.totalMessageLength == null || this.totalMessageLength.length() != 4) {
            throw new ComponentRuntimeException("报文总长度为4位数字");
        }
        byte[] f3 = this.totalMessageLength.getBytes();
        buffer.put(f3);
        logger.debug("--> 请求报文总长度:{}, Hex:{}", this.totalMessageLength, CupsUtils.bytes2HexString(f3));

        // F4
        if (this.destinationId == null || this.destinationId.getBytes().length > 11) {
            throw new ComponentRuntimeException("目的ID最长11位字母、数字、特殊字符");
        }
        String destId = CupsUtils.rightPad(this.destinationId, 11 - this.destinationId.getBytes().length, " ");
        byte[] f4 = destId.getBytes();
        buffer.put(f4);

        // F5
        if (this.sourceId == null || this.sourceId.getBytes().length > 11) {
            throw new ComponentRuntimeException("源ID最长11位字母、数字、特殊字符");
        }
        String sid = CupsUtils.rightPad(this.sourceId, 11 - this.sourceId.getBytes().length, " ");
        byte[] f5 = sid.getBytes();
        buffer.put(f5);

        // F6
        if (this.reservedForUse == null || this.reservedForUse.length() != 24) {
            throw new ComponentRuntimeException("头保留使用部分24位二进制数");
        }
        byte[] f6 = CupsUtils.binaryStr2Bytes(this.reservedForUse);
        buffer.put(f6);

        // F7
        if (this.batchNumber == null || this.batchNumber.length() != 8) {
            throw new ComponentRuntimeException("批次号需8位二进制数");
        }
        byte f7 = CupsUtils.binaryStr2Byte(this.batchNumber);
        buffer.put(f7);

        // F8
        byte[] f8 = this.transactionInfo.getBytes();
        if (f8.length != 8) {
            throw new ComponentRuntimeException("交易信息需8位字母、数字、特殊字符");
        }
        buffer.put(f8);

        // F9
        if (this.userInfo == null || this.userInfo.length() != 8 || !CupsUtils.isBinaryStr(this.userInfo)) {
            throw new ComponentRuntimeException("用户信息需8位二进制数");
        }
        byte f9 = CupsUtils.binaryStr2Byte(this.userInfo);
        buffer.put(f9);

        // F10
        if (this.rejectCode == null || this.rejectCode.length() != 5) {
            throw new ComponentRuntimeException("拒绝码5位数字");
        }
        byte[] f10 = this.rejectCode.getBytes();
        buffer.put(f10);

        // Total
        byte[] bytes = buffer.array();
        if (bytes.length != HEADER_TOTAL_BYTE_NUM) {
            throw new ComponentRuntimeException("报文头组装错误，报文头必须占" + HEADER_TOTAL_BYTE_NUM + "个字节");
        }
        return bytes;
    }

    public static CupsHeader form(byte[] headerBytes) {
        CupsHeader header = new CupsHeader();
        if (headerBytes[0] == HEADER_TOTAL_BYTE_NUM_0) {
            header.headerLength = HEADER_TOTAL_BYTE_NUM;
        } else {
            header.headerLength = headerBytes[0];
        }
        header.headerFlagAndVersion = CupsUtils.bytes2BinaryStr(headerBytes[1]);
        byte[] tmlBytes = ArrayUtils.subarray(headerBytes, 2, 6);
        header.totalMessageLength = new String(tmlBytes, StandardCharsets.UTF_8);
        logger.debug("<-- 响应报文总长度:{}, Hex:{}", header.totalMessageLength, CupsUtils.bytes2HexString(tmlBytes));
        header.destinationId = new String(ArrayUtils.subarray(headerBytes, 6, 17), StandardCharsets.UTF_8);
        header.sourceId = new String(ArrayUtils.subarray(headerBytes, 17, 28), StandardCharsets.UTF_8);
        header.reservedForUse = CupsUtils.bytes2BinaryStr(ArrayUtils.subarray(headerBytes, 28, 31));
        header.batchNumber = CupsUtils.bytes2BinaryStr(ArrayUtils.subarray(headerBytes, 31, 32));
        header.transactionInfo = new String(ArrayUtils.subarray(headerBytes, 32, 40), StandardCharsets.UTF_8);
        header.userInfo = CupsUtils.bytes2BinaryStr(ArrayUtils.subarray(headerBytes, 40, 41));
        byte[] rcBytes = ArrayUtils.subarray(headerBytes, 41, 46);
        header.rejectCode = new String(rcBytes, StandardCharsets.UTF_8);
        logger.debug("<-- 响应拒绝码:{}, Hex:{}", header.rejectCode, CupsUtils.bytes2HexString(rcBytes));
        return header;
    }

    /**
     * 初始化请求头
     */
    public void initRequestHeader() {
        this.reservedForUse = "000000000000000000000000";
        this.batchNumber = "00000000";
        this.transactionInfo = "00000000";
        this.rejectCode = "00000";
    }

    public int getHeaderLength() {
        return headerLength;
    }

    public void setHeaderLength(int headerLength) {
        this.headerLength = headerLength;
    }

    public String getHeaderFlagAndVersion() {
        return headerFlagAndVersion;
    }

    public void setHeaderFlagAndVersion(String headerFlagAndVersion) {
        this.headerFlagAndVersion = headerFlagAndVersion;
    }

    public String getTotalMessageLength() {
        return totalMessageLength;
    }

    public void setTotalMessageLength(String totalMessageLength) {
        this.totalMessageLength = totalMessageLength;
    }

    public String getDestinationId() {
        return destinationId;
    }

    public void setDestinationId(String destinationId) {
        this.destinationId = destinationId;
    }

    public String getSourceId() {
        return sourceId;
    }

    public void setSourceId(String sourceId) {
        this.sourceId = sourceId;
    }

    public String getReservedForUse() {
        return reservedForUse;
    }

    public void setReservedForUse(String reservedForUse) {
        this.reservedForUse = reservedForUse;
    }

    public String getBatchNumber() {
        return batchNumber;
    }

    public void setBatchNumber(String batchNumber) {
        this.batchNumber = batchNumber;
    }

    public String getTransactionInfo() {
        return transactionInfo;
    }

    public void setTransactionInfo(String transactionInfo) {
        this.transactionInfo = transactionInfo;
    }

    public String getUserInfo() {
        return userInfo;
    }

    public void setUserInfo(String userInfo) {
        this.userInfo = userInfo;
    }

    public String getRejectCode() {
        return rejectCode;
    }

    public void setRejectCode(String rejectCode) {
        this.rejectCode = rejectCode;
    }
}
