package com.jeequan.jeepay.pay.aop;

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.JSONObject;
import com.jeequan.jeepay.converter.MchInfoConverter;
import com.jeequan.jeepay.core.beans.RequestKitBean;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.openapi.AccessReq;
import com.jeequan.jeepay.core.model.openapi.AccessResp;
import com.jeequan.jeepay.core.utils.Base64;
import com.jeequan.jeepay.db.entity.MchAppEntity;
import com.jeequan.jeepay.db.entity.PackageOrder;
import com.jeequan.jeepay.db.entity.ProductInfo;
import com.jeequan.jeepay.pay.anno.OpenLabel;
import com.jeequan.jeepay.service.impl.MchAppService;
import com.jeequan.jeepay.service.impl.MchConfigService;
import com.jeequan.jeepay.service.impl.PackageOrderService;
import com.jeequan.jeepay.service.impl.ProductInfoService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.List;

/***
 * 方法级日志切面组件
 *
 * @author terrfly
 * @since 2023/9/7 8:29
 */
@Slf4j
@Component
@Aspect
public class OpenLabelAop {


    @Autowired
    private RequestKitBean requestKitBean;

    @Autowired
    private MchAppService mchAppService;

    @Autowired
    private MchConfigService mchConfigService;

    @Autowired
    private MchInfoConverter mchInfoConverter;

    @Autowired
    private PackageOrderService packageOrderService;

    @Autowired
    private ProductInfoService productInfoService;

    /**
     * 切点
     */
    @Pointcut("@annotation(com.jeequan.jeepay.pay.anno.OpenLabel)")
    public void methodCachePointcut() {
    }

    /**
     * 切面
     *
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("methodCachePointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        JSONObject reqParamJSON = requestKitBean.getReqParamJSON();
        AccessReq accessReq = reqParamJSON.toJavaObject(AccessReq.class);

        String appId = accessReq.getAppId();
        if (ObjUtil.isEmpty(appId)) {
            return AccessResp.fail(accessReq, "非法请求, 缺少应用[appId]参数");
        }

        MchAppEntity mchApp = mchAppService.getById(appId);
        if (mchApp == null) {
            return AccessResp.fail(accessReq, "应用[appId]参数非法");
        }

        // 先校验签名
        try {
            checkSign(accessReq, mchApp);
        } catch (Exception e) {
            return AccessResp.fail(accessReq, e.getMessage());
        }

        // 权限的Key值
        String permissionKey = getAnnotationValue(point);

        accessReq.setAppInfo(mchInfoConverter.toModel(mchApp));

        if (!ObjUtil.isEmpty(permissionKey)) {
//            //校验应用的产品权限 TODO 方案的暂时没处理
//            //判断指定产品和指定应用是否开通
//            ProductInfo productInfo = productInfoService.getOne(ProductInfo.gw().eq(ProductInfo::getApiCode, permissionKey));
//            if (productInfo == null) {
//                return AccessResp.forbidden("未获取到商品信息！");
//            }
//            List<PackageOrder> packageOrderList = packageOrderService.lambdaQuery()
//                    .eq(PackageOrder::getAppId, mchApp.getAppId())
//                    .eq(PackageOrder::getProductId, productInfo.getProductId())// TODO: 如果兼容方案的话加上or语句 packageId
//                    .eq(PackageOrder::getMchNo, mchApp.getMchNo())
//                    .eq(PackageOrder::getState, PackageOrder.STATE_SUCCESS).list();
//
//            if (packageOrderList.isEmpty()) {
//                return AccessResp.forbidden("当前应用未开通对应的产品功能, 请前往商家端后台开通对应的产品");
//            }
            boolean hasApiEnt = mchConfigService.queryMchAppHasApiEnt(mchApp.getMchNo(), mchApp.getAppId(), permissionKey);
            if (!hasApiEnt) {
                return AccessResp.forbidden("应用暂未开通接口权限");
            }
        }
        try {
            Object proceed = point.proceed();
            if (proceed instanceof AccessResp) {
                return proceed;
            }

            return AccessResp.success(accessReq, proceed);
        } catch (Exception e) {
            log.warn("请求异常", e);
            return AccessResp.fail(accessReq, e.getMessage());
        }
    }

    private void checkSign(AccessReq accessReq, MchAppEntity mchApp) {
        String sign = accessReq.getSign();
        String signType = accessReq.getSignType();

        if (ObjUtil.isEmpty(sign)) {
            throw new BizException("缺少签名[sign]");
        }

        if (ObjUtil.isEmpty(signType)) {
            throw new BizException("缺少签名类型[signType]");
        }

        String appSecret = mchApp.getAppSecret();

        if ("MD5".equals(signType)) {
            String signContent = accessReq.signContent() + "&appSecret=" + appSecret;
            log.info("验签串: {}", signContent);

            String signVerify = SecureUtil.md5(signContent);

            Assert.isTrue(signVerify.equals(sign), "验签失败");
        } else if ("RSA2".equals(signType)) {
            String pubKey = mchApp.getAppRsa2PublicKey();
            Sign signUtil = SecureUtil.sign(SignAlgorithm.SHA256withRSA, null, pubKey);
            String signContent = accessReq.signContent();
            byte[] signBytes = Base64.decodeBase64(accessReq.getSign().getBytes(StandardCharsets.UTF_8));
            try {
                boolean verify = signUtil.verify(signContent.getBytes(StandardCharsets.UTF_8), signBytes);
                Assert.isTrue(verify, "验签失败");
            } catch (Exception e) {
                log.info("验签异常", e);
                throw new BizException("验签异常");
            }
        } else {
            throw new BizException("非法的签名类型[signType]");
        }
    }

    /**
     * 获取方法中的中文备注
     *
     * @param joinPoint 请求节点信息
     */
    public static String getAnnotationValue(JoinPoint joinPoint) throws Exception {

        Signature sig = joinPoint.getSignature();
        Method m = joinPoint.getTarget().getClass().getMethod(joinPoint.getSignature().getName(), ((MethodSignature) sig).getParameterTypes());

        OpenLabel methodCache = m.getAnnotation(OpenLabel.class);
        if (methodCache != null) {
            return methodCache.value();
        }

        return "";
    }

}
