package com.jeequan.jeepay.core.model.openapi;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.annotation.JSONField;
import com.jeequan.jeepay.core.entity.MchApp;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import javax.xml.bind.DatatypeConverter;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * TODO
 * 网关api统一返回参数
 * @author crystal
 * @since 2024/4/7 14:32
 */
@Slf4j
@Data
public class AccessResp implements Serializable {

    private String code;

    private String msg;

    private String timestamp;

    private String sign;

    private String signType;

    /**
     * 业务响应参数
     */
    private String bizData;


    public static AccessResp forbidden(String msg) {
        AccessResp accessResp = new AccessResp();
        accessResp.setCode(AccessError.FORBIDDEN);
        accessResp.setTimestamp(DateUtil.format(new Date(), "yyyyMMddHHmmss"));
        accessResp.setMsg(msg);
        return accessResp;
    }

    public static AccessResp forbidden() {
        AccessResp accessResp = new AccessResp();
        accessResp.setCode(AccessError.FORBIDDEN);
        accessResp.setTimestamp(DateUtil.format(new Date(), "yyyyMMddHHmmss"));
        accessResp.setMsg("无权访问");
        return accessResp;
    }

    public static AccessResp fail(String code,String message) {
        AccessResp accessResp = new AccessResp();
        accessResp.setCode(code);
        accessResp.setTimestamp(DateUtil.format(new Date(), "yyyyMMddHHmmss"));
        accessResp.setMsg(message);

        return accessResp;
    }

    public static AccessResp fail(AccessReq request, Object data) {
        log.error(JSON.toJSONString(data));
        return new AccessResp(AccessError.COMMON_ERROR, data, request);
    }

    public static AccessResp fail(AccessReq req, String message) {
        log.error(message);
        Map<String, Object> data = new HashMap<>();
        data.put("note", message);
        return new AccessResp(AccessError.commonError(message), data, req);
    }

    public static AccessResp success(AccessReq req, Object data) {
        return new AccessResp(AccessError.SUCCESS, data, req);
    }


    private AccessResp() {}

    @JSONField(serialize = false)
    private String getSignContent() {
        return  "bizData=" + bizData + "&code=" + code + "&msg=" + msg  + "&signType=" + signType + "&timestamp=" + timestamp;
    }

    public AccessResp(AccessError respEnum, Object data, AccessReq req) {
        this.code = respEnum.getCode();
        this.msg = respEnum.getMsg();
        this.timestamp = DateUtil.format(new Date(), "yyyyMMddHHmmss");
        this.bizData = JSON.toJSONString(data);
        MchApp appInfo = req.getAppInfo();
        String signType = req.getSignType();
        this.signType = req.getSignType();

        if (req.getAppId() == null || appInfo == null) {
            return;
        }

        if (MchApp.SIGN_TYPE_MD5.equals(signType)) {
            String signContent = getSignContent() + "&appSecret=" + appInfo.getAppSecret();
            this.sign = SecureUtil.md5(signContent);
            this.signType = "MD5";
            return;
        } else if (MchApp.SIGN_TYPE_RSA2.equals(signType)) {;
            this.signType = "RSA2";
            try {
                String signContent = getSignContent();
                Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, req.getPlatPriKey(), null);
                byte[] signBytes = sign.sign(signContent);
                this.sign = DatatypeConverter.printBase64Binary(signBytes);
            } catch (Exception e) {
                log.error("数据加签异常:", e);
                log.error("加签源数据, {}", getSignContent());
            }
        } else if (ObjUtil.isEmpty(signType)) {
            this.signType = "RSA2";
            // 签名类型为空时，看应用中的签名配置
            List<String> appSignTypeList = JSONArray.parseArray(appInfo.getAppSignType(), String.class);
            boolean hasMD5 = false;
            for (String item : appSignTypeList) {
                if (item.equals("MD5")) {
                    hasMD5 = true;
                    break;
                }
            }

            if (hasMD5) {
                this.signType = "MD5";
                String signContent = getSignContent() + "&appSecret=" + appInfo.getAppSecret();
                this.sign = SecureUtil.md5(signContent);
            } else {
                try {
                    String signContent = getSignContent();
                    Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, req.getPlatPriKey(), null);
                    byte[] signBytes = sign.sign(signContent);
                    this.sign = DatatypeConverter.printBase64Binary(signBytes);
                } catch (Exception e) {
                    log.error("数据加签异常:", e);
                    log.error("加签源数据, {}", getSignContent());
                }
            }
        }

    }

    public AccessResp(AccessError respEnum, Object data, String priKey, String aesKey) {
        this.code = respEnum.getCode();
        this.msg = respEnum.getMsg();
        this.timestamp = DateUtil.format(new Date(), "yyyyMMddHHmmss");

        try {
            this.bizData = JSON.toJSONString(data);
            String signContent = getSignContent();
            Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, priKey, null);
            byte[] signBytes = sign.sign(signContent);
            this.sign = DatatypeConverter.printBase64Binary(signBytes);
        } catch (Exception e) {
            log.error("数据加签异常: ", e);
            log.error("加签源数据, {}", getSignContent());
        }
    }

    public AccessResp(String code, String message, String priKey) {
        this.code = code;
        this.msg = message;
        this.timestamp = DateUtil.format(new Date(), "yyyyMMddHHmmss");

        if (priKey == null) {
            return;
        }

        try {
            String signContent = getSignContent();
            Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, priKey, null);
            byte[] signBytes = sign.sign(signContent);
            this.sign = DatatypeConverter.printBase64Binary(signBytes);
        } catch (Exception e) {
            log.error("数据加签异常: ", e);
            log.error("加签源数据, {}", getSignContent());
        }
    }

    public String toJSONString() {
        return JSON.toJSONString(this);
    }
}
