package com.zzwtec.wechat.rpc;

import java.util.HashMap;
import java.util.Map;

import static com.xiaoleilu.hutool.util.ArrayUtil.isEmpty;
import static com.xiaoleilu.hutool.util.StrUtil.isBlank;
import static com.zzwtec.wechat.util.security.EncoderHandler.*;


/**
 * 参数签名
 */
public class Signature {

    private String appid;
    private String secretKey;
    private String headerSign;
    private String signAlgorithm;

    private static final String APPID = "appid";
    private static final String SIGN = "sign";
    public static final String ZZW_SIGN_HEADER = "zzw-sign";

    public Signature(String appid, String secretKey, String headerSign, String signAlgorithm) {
        this.appid = appid;
        this.secretKey = secretKey;
        this.headerSign = headerSign;
        this.signAlgorithm = signAlgorithm;
    }

    public String getAppid() {
        return appid;
    }

    public Signature setAppid(String appid) {
        this.appid = appid;
        return this;
    }

    public String getSecretKey() {
        return secretKey;
    }

    public Signature setSecretKey(String secretKey) {
        this.secretKey = secretKey;
        return this;
    }

    public String getHeaderSign() {
        return headerSign;
    }

    public Signature setHeaderSign(String headerSign) {
        this.headerSign = headerSign;
        return this;
    }

    public String getSignAlgorithm() {
        return signAlgorithm;
    }

    public Signature setSignAlgorithm(String signAlgorithm) {
        this.signAlgorithm = signAlgorithm;
        return this;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("Signature{");
        sb.append("appid='").append(appid).append('\'');
        sb.append(", secretKey='").append(secretKey).append('\'');
        sb.append(", headerSign='").append(headerSign).append('\'');
        sb.append(", signAlgorithm='").append(signAlgorithm).append('\'');
        sb.append('}');
        return sb.toString();
    }

    public boolean validateSign(String body) {
        notNull(headerSign, "headerSign is empty");
        String calculateSign = sign(body);
        return calculateSign.equalsIgnoreCase(headerSign);
    }

    public String sign(String body) {
        notNull(secretKey, "secretKey is empty");
        notNull(signAlgorithm, "signAlgorithm is empty");
        String plain = body + secretKey;
        if (signAlgorithm.equals("MD5")) {
            return MD5(plain);
        } else if (signAlgorithm.equals("SHA1")) {
            return SHA1(plain);
        } else if (signAlgorithm.equals("SHA512")) {
            return SHA512(plain);
        }
        throw new IllegalStateException("never reach here");
    }

    public static String toHeader(Signature signature, String body) {
        notNull(signature.appid, "appid is empty");
        String sign = signature.sign(body);
        StringBuilder builder = new StringBuilder(APPID.length() + signature.getAppid().length() + SIGN.length() + sign.length() + 3);
        builder.append(APPID).append("=").append(signature.getAppid()).append(";").append(SIGN).append("=").append(sign);
        return builder.toString();
    }

    public static Signature parseHeader(String signHeader) {
        if (isBlank(signHeader)) {
            return null;
        }
        String[] signs = signHeader.split(";");
        if (isEmpty(signs)) {
            return null;
        }
        Map<String, String> map = new HashMap<>();
        for (String sign : signs) {
            String[] kv = sign.split("=");
            if (isEmpty(kv) || kv.length != 2) {
                continue;
            }
            if (kv[0].equals(APPID)) {
                map.put(kv[0], kv[1]);
            } else if (kv[0].equals(SIGN)) {
                map.put(kv[0], kv[1]);
            }
        }
        if (!map.containsKey(APPID) || !map.containsKey(SIGN)) {
            return null;
        }
        return new Signature(map.get(APPID), null, map.get(SIGN), null).setSignAlgorithm();
    }

    public static void notNull(Object object, String message) {
        if (object == null) {
            throw new IllegalArgumentException(message);
        }
    }

    private Signature setSignAlgorithm() {
        if (headerSign.length() == 32) {
            this.signAlgorithm = "MD5";
        } else if (headerSign.length() == 40) {
            this.signAlgorithm = "SHA1";
        } else if (headerSign.length() == 128) {
            this.signAlgorithm = "SHA512";
        } else {
            throw new IllegalArgumentException("unsupported algorithm");
        }
        return this;
    }
}
