package com.cpop.pay.framewok.core.request.ecpp;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.DESede;
import com.cpop.core.base.exception.ServiceException;
import com.cpop.core.utils.SpringUtils;
import com.cpop.pay.framewok.anno.EcppUnSignField;
import com.cpop.pay.framewok.config.ecpp.EcppConfiguration;
import com.cpop.pay.framewok.config.ecpp.EcppProperties;
import com.cpop.pay.framewok.handler.ecpp.EcppHandler;
import com.google.common.base.Joiner;
import com.google.gson.Gson;
import jodd.util.StringUtil;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @author DB
 * @version 1.0.0
 * @since 2024-01-29 15:42
 */
@Data
@Slf4j
@NoArgsConstructor
public class EcppBaseRequest {
    {
        jSessionType = "00";
    }

    public <T extends EcppVerifyRequest> EcppBaseRequest(T data) {
        setContent(data);
    }

    /**
     * 交易类型
     */
    private String jSessionType;

    /**
     * 交易id
     */
    private String jSessionId;

    /**
     *
     */
    private String type = "1";

    /**
     * 内容
     */
    private String content;

    private <T extends EcppVerifyRequest> void setContent(T data) {
        String unSignStr = getUnSignStr(data);
        EcppConfiguration ecppConfiguration = SpringUtils.getBean(EcppConfiguration.class);
        EcppProperties ecppProperties = ecppConfiguration.getEcppProperties();
        if (StringUtil.isNotEmpty(unSignStr)) {
            data.setSignature(ecppConfiguration.genSha256HexSign(ecppProperties.getPrivateKey(), unSignStr));
        }
        try {
            //检查是否超时
            if(!ecppConfiguration.checkKeyTime()) {
                SpringUtils.getBean(EcppHandler.class).getSecurityKey();
            }
            String encryptContent = encrypt(new Gson().toJson(data), ecppConfiguration.getSessionKey(), ecppConfiguration.getSessionIv());
            this.jSessionId = ecppConfiguration.getJSessionId();
            this.content = encryptContent;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private String encrypt(String content, String key, String iv) throws Exception {
        if (content == null) {
            return "";
        }
        if (ObjectUtil.isEmpty(key)) {
            throw new ServiceException("网络异常");
        }
        DESede deSede = new DESede(Mode.CBC, Padding.PKCS5Padding, key.getBytes(), StrUtil.sub(iv, 0, 8).getBytes());
        return deSede.encryptBase64(content);
    }

    private <T> String getUnSignStr(T data) {
        List<String> templist = new ArrayList<>();
        Class<?> clazz = data.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            try {
                if(field.getAnnotation(EcppUnSignField.class) != null) {
                    continue;
                }
                String methodName = "get" + firstCharUppercase(field.getName());
                Method getMethod = clazz.getDeclaredMethod(methodName);
                Object prop = getMethod.invoke(data);
                if (prop == null) {
                    templist.add(field.getName() + "=");
                    continue;
                }
                if (prop instanceof Collection) {
                    for (Object _prop : (Collection) prop) {
                        templist.add(getUnSignStr(_prop));
                    }
                    continue;
                }
                templist.add(field.getName() + "=" + prop);
            } catch (Exception ignored) {
            }
        }
        return Joiner.on('&').join(templist);
    }

    private String firstCharUppercase(String propName) {
        char[] chars = propName.toCharArray();
        chars[0] -= 32;
        return String.valueOf(chars);
    }
}
