package com.jeequan.jeepay.pay.service.openapi;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.URLUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.converter.MchInfoConverter;
import com.jeequan.jeepay.core.beans.RequestKitBean;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.MchApp;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.entity.MchInfo;
import com.jeequan.jeepay.core.exception.AccessMsgException;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.exception.openapi.ParamException;
import com.jeequan.jeepay.core.interfaces.paychannel.IPaymentService;
import com.jeequan.jeepay.core.model.DBApplicationConfig;
import com.jeequan.jeepay.core.model.QRCodeParams;
import com.jeequan.jeepay.core.model.applyment.PaywayFee;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.oauth2.AlipayOauth2Params;
import com.jeequan.jeepay.core.model.oauth2.Oauth2Params;
import com.jeequan.jeepay.core.model.oauth2.WxpayOauth2Params;
import com.jeequan.jeepay.core.model.openapi.AccessError;
import com.jeequan.jeepay.core.model.openapi.biz.app.AppBizResp;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelAppletPayMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.service.IOpenApiService;
import com.jeequan.jeepay.core.utils.*;
import com.jeequan.jeepay.db.entity.*;
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
import com.jeequan.jeepay.pay.model.OpenApiExt;
import com.jeequan.jeepay.service.impl.*;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.service.wxmp.WxTgAppletService;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.util.Date;

/**
 * TODO
 *
 * @author crystal
 * @date 2024/4/8 15:16
 */
public abstract class AbstractPayOpenApiService extends AbstractPayOrderController implements IOpenApiService {

    public static final String ALIPAY_START_APP_URL = "https://ds.alipay.com/?scheme=";

    public static final String SCHEME = "alipays://platformapi/startapp?appId=10000007&qrcode=";

    @Autowired
    private ConfigContextQueryService configContextQueryService;
    @Autowired
    private PayOrderService payOrderService;
    @Autowired
    private RateConfigService rateConfigService;
    @Autowired
    private PayWayService payWayService;
    @Autowired
    private RequestKitBean requestKitBean;
    @Autowired
    private MchStoreService mchStoreService;
    @Autowired
    private MchInfoConverter mchInfoConverter;
    @Autowired
    private SysConfigService sysConfigService;
    @Autowired
    private PayInterfaceConfigService payInterfaceConfigService;
    @Autowired
    private WxTgAppletService wxTgAppletService;
    @Autowired
    private RouteService routeService;
    @Autowired
    private RouteRecordService routeRecordService;

    protected OpenApiExt checkMchInfo(UnifiedOrderRQ rq, MchApp appInfo){
        OpenApiExt apiExt = new OpenApiExt();
        if(StringUtils.isEmpty(rq.getStoreId()) && StringUtils.isEmpty(rq.getRouteNo())){
            throw new ParamException("门店编号和路由编号不能同时为空");
        }
        MchStore store = null;
        if(StringUtils.isNotEmpty(rq.getStoreId())){
            store = mchStoreService.getById(rq.getStoreId());
        }else{
            String payWayCode = StringUtils.isEmpty(rq.getWayCode()) || rq.getWayCode().contains("WX") ? CS.PAY_WAY_CODE_TYPE.WECHAT:CS.PAY_WAY_CODE_TYPE.ALIPAY;
            RouteManage manage = routeService.doRoute(rq.getRouteNo(), rq.getAmount(), payWayCode);
            store = mchStoreService.getById(manage.getStoreId());
            apiExt.setRouteManage(manage);
        }
        if(store == null){
            throw new AccessMsgException(AccessError.dataError("门店数据获取失败"));
        }
        //获取支付参数 (缓存数据) 和 商户信息
        MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(appInfo.getMchNo(), appInfo.getAppId(),store.getMchApplyId());
        if(mchAppConfigContext == null){
            throw new AccessMsgException(AccessError.dataError("商户数据获取失败"));
        }
        mchAppConfigContext.setMchStore(store);
        MchApplyment applyment = mchAppConfigContext.getMchApplyment();
        if(applyment == null){
            throw new AccessMsgException(AccessError.dataError("商户信息异常"));
        }
        if(MchApplyment.STATE_SUCCESS != applyment.getState() && MchApplyment.STATE_SUCCESS_NEED_SECOND_VERIFY != applyment.getState()){
            throw new AccessMsgException(AccessError.stateError("商户状态不可用"));
        }
        if(CS.IF_CODE.ZFTPAY.equals(applyment.getIfCode())){
            rq.setIsReqChannel(true);
            rq.setIsAppletCheck(false);
            if(CS.PAY_WAY_CODE.QR_CASHIER.equals(rq.getWayCode())){
                rq.setWayCode(CS.PAY_WAY_CODE.ALI_PC);
            }
        }
        //拉卡拉B2B收银台不检验小程序参数
        if(CS.IF_CODE.LKLSB2BPAY.equals(applyment.getIfCode())){
            rq.setIsAppletCheck(false);
        }
        apiExt.setUrq(rq.buildBizRQ());
        apiExt.setConfigContext(mchAppConfigContext);
        return apiExt;
    }

    protected void checkPay(MchAppConfigContext mchAppConfigContext,UnifiedOrderRQ rq, MchApp appInfo){
        if(StringUtils.isNotEmpty(rq.getNotifyUrl()) && !StringKit.isAvailableUrl(rq.getNotifyUrl())){
            throw new ParamException("异步通知地址协议仅支持http:// 或 https:// !");
        }
        if(StringUtils.isNotEmpty(rq.getReturnUrl()) && !StringKit.isAvailableUrl(rq.getReturnUrl())){
            throw new ParamException("同步通知地址协议仅支持http:// 或 https:// !");
        }
        if(payOrderService.count(PayOrder.gw().eq(PayOrder::getMchOrderNo, rq.getMchOrderNo())) > 0){
            throw new ParamException("商户订单号已存在");
        }
        //是否校验商户自己的小程序参数
        if(rq.getIsAppletCheck()){
            // 查询已经配置的信息
            PayInterfaceConfig payInterfaceConfig = payInterfaceConfigService.getByInfoIdAndIfCode(CS.SYS_ROLE_TYPE.MCH_APP_OAUTH2, appInfo.getAppId(), CS.IF_CODE.WXPAY);
            if(payInterfaceConfig == null){
                throw new AccessMsgException(AccessError.configError("应用小程序支付参数配置异常"));
            }
            WxpayOauth2Params isObj = JSON.parseObject(payInterfaceConfig.getIfParams(),WxpayOauth2Params.class);
            if(isObj.getIsUseSubmchAccount() == 1 && StringUtils.isEmpty(isObj.getLiteAppId())){
                throw new AccessMsgException(AccessError.configError("当前应用未关联小程序支付参数，请联系客服处理"));
            }
            wxTgAppletService.getThirdAccessToken(isObj.getLiteAppId());
        }
        checkRate(mchAppConfigContext,appInfo,rq.getWayCode());
    }

    public void checkRate(MchAppConfigContext mchAppConfigContext,MchApp appInfo,String wayCode){
        if(!CS.PAY_WAY_CODE.QR_CASHIER.equals(wayCode)){
            PaywayFee paywayFee = rateConfigService.queryCreateOrderPaywayFee(mchAppConfigContext.getMchApplyment().getApplyId(), mchAppConfigContext.getMchApplyment().getIsvNo(), appInfo.getAppId(), mchAppConfigContext.getMchApplyment().getIfCode(), wayCode);
            if(paywayFee == null){
                throw new BizException("商户费率未配置");
            }
            mchAppConfigContext.setTradeRate(paywayFee);
            if(CS.SETTLEMENT_TYPE.D0.equals(mchAppConfigContext.getMchApplyment().getSettlementType())){
                PaywayFee d0PaywayFee = rateConfigService.queryCreateOrderPaywayFee(mchAppConfigContext.getMchApplyment().getApplyId(), mchAppConfigContext.getMchApplyment().getIsvNo(), appInfo.getAppId(), mchAppConfigContext.getMchApplyment().getIfCode(), CS.PAY_WAY_CODE.D0);
                if(d0PaywayFee == null){
                    throw new AccessMsgException(AccessError.commonError("D0费率未配置生效"));
                }
                mchAppConfigContext.setD0Rate(d0PaywayFee);
            }
        }
    }

    /**
     * 获取支付路由
     * @param mchAppConfigContext
     * @return
     */
    protected IPaymentService getPayRoute(MchAppConfigContext mchAppConfigContext) {
        IPaymentService paymentService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(mchAppConfigContext.getMchApplyment().getIfCode()) + "PaymentService", IPaymentService.class);
        if(paymentService == null){
            throw new AccessMsgException(AccessError.configError("通道接口未配置"));
        }
        if(mchAppConfigContext.getMchType() == MchInfo.TYPE_NORMAL){
            if(configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), mchAppConfigContext.getMchApplyment().getIfCode()) == null){
                throw new AccessMsgException(AccessError.configError("商户应用参数未配置"));
            }
        }else if(mchAppConfigContext.getMchType() == MchInfo.TYPE_ISVSUB){ //特约商户
            if (configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment().getIsvNo(), mchAppConfigContext.getMchApplyment().getIfCode()) == null) {
                throw new AccessMsgException(AccessError.configError("服务商参数未配置"));
            }
        }
        return paymentService;
    }

    public PayOrder genPayOrder(UnifiedOrderRQ rq, MchAppConfigContext mchAppConfigContext,RouteManage manage){
        MchInfo mchInfo = mchAppConfigContext.getMchInfo();
        MchApp mchApp = mchAppConfigContext.getMchApp();
        MchStore mchStore = mchInfoConverter.toDbEntity(mchAppConfigContext.getMchStore());
        PayOrder payOrder = new PayOrder();
        payOrder.setPayOrderId(SeqKit.genPayOrderId());
        payOrder.setMchNo(mchInfo.getMchNo());
        payOrder.setEpUserId(mchInfo.getCreatedUid());
        payOrder.setIsvNo(mchInfo.getIsvNo());
        payOrder.setMchName(mchAppConfigContext.getMchApplyment().getMchShortName());
        payOrder.setMchType(mchInfo.getType());
        payOrder.setMchOrderNo(rq.getMchOrderNo());
        payOrder.setAppId(mchApp.getAppId());
        payOrder.setIfCode(mchAppConfigContext.getMchApplyment().getIfCode());
        payOrder.setWayCode(rq.getWayCode());
        payOrder.setAgentNo(mchInfo.getAgentNo());
        payOrder.setTopAgentNo(mchInfo.getTopAgentNo());
        payOrder.setSettleType(mchAppConfigContext.getMchApplyment().getSettlementType());
        PayWay payWay = payWayService.getById(rq.getWayCode());
        payOrder.setWayCodeType(payWay == null ? CS.PAY_WAY_CODE_TYPE.OTHER :payWay.getWayType());
        payOrder.setAmount(rq.getAmount());
        payOrder.setFindAmt(rq.getAmount());
        payOrder.setStoreId(mchStore.getStoreId());
        payOrder.setStoreName(mchStore.getStoreName());
        payOrder.setLng(mchStore.getLng());
        payOrder.setLat(mchStore.getLat());
        payOrder.setAddress(mchStore.getAddress());
        payOrder.setStoreUserId(rq.getStoreUserId());
        payOrder.setMchExtNo(mchAppConfigContext.getMchApplyment().getApplyId());
        if(mchAppConfigContext.getTradeRate() != null){
            MutablePair<String, Long> feeAndSnapshot = mchAppConfigContext.getTradeRate().calFeeAndSnapshot(payOrder.getFindAmt());
            payOrder.setMchFeeRate(feeAndSnapshot.left);
            //TODO 银盛特殊性 保底收 0.01
            if(CS.IF_CODE.YSPAY.equals(payOrder.getIfCode())){
                feeAndSnapshot.right = feeAndSnapshot.right == BigDecimal.ZERO.longValue() ? BigDecimal.ONE.longValue() : feeAndSnapshot.right;
            }
            payOrder.setMchFeeAmount(feeAndSnapshot.right); //商户手续费,单位分
            payOrder.setMchOrderFeeAmount(feeAndSnapshot.right); //商户手续费,单位分
        }else{
            payOrder.setMchFeeRate(""); //预下单模式， 按照0计算入库， 后续进行更新
            payOrder.setMchFeeAmount(0L); //商户手续费,单位分
            payOrder.setMchOrderFeeAmount(0L); //商户手续费,单位分
        }
        if(mchAppConfigContext.getD0Rate() != null && CS.SETTLEMENT_TYPE.D0.equals(mchAppConfigContext.getMchApplyment().getSettlementType())){
            MutablePair<String, Long> feeAndSnapshot = mchAppConfigContext.getD0Rate().calFeeAndSnapshot(payOrder.getFindAmt());
            payOrder.setCashRate(mchAppConfigContext.getD0Rate().getFeeRate().multiply(BigDecimal.valueOf(100)));
            Long cashFee = feeAndSnapshot.right;
            if(cashFee == 0){
                cashFee = BigDecimal.ONE.longValue();
            }
            payOrder.setCashFee(cashFee); //商户手续费,单位分
            //拉卡拉D0 除了支付宝或者微信以外银联交易都是D1 或者小于10元都是D1
            if(CS.IF_CODE.LKLSPAY.equals(payOrder.getIfCode()) && (!CS.PAY_WAY_CODE_TYPE.WECHAT.equals(payOrder.getWayCodeType()) && !CS.PAY_WAY_CODE_TYPE.ALIPAY.equals(payOrder.getWayCodeType())) || payOrder.getFindAmt().longValue() < 10){
                payOrder.setCashFee(0L);
                payOrder.setCashRate(BigDecimal.ZERO);
            }
        }
        payOrder.setCurrency(rq.getCurrency()); //币种
        payOrder.setState(PayOrder.STATE_INIT); //订单状态, 默认订单生成状态
        payOrder.setClientIp(StringUtils.defaultIfEmpty(rq.getClientIp(), requestKitBean.getClientIp()));
        payOrder.setSubject(rq.getSubject()); //商品标题
        payOrder.setBody(rq.getBody());
        payOrder.setChannelUser(rq.getChannelUserId());
//        payOrder.setExtParam(rq.getExtParam());
        payOrder.setNotifyUrl(rq.getNotifyUrl());
        payOrder.setReturnUrl(rq.getReturnUrl());
        payOrder.setBuyerRemark(rq.getBuyerRemark());
        // 服务商机构号或子商户号
        payOrder.setChannelMchNo(mchAppConfigContext.getMchApplyment().getChannelMchNo());
        Date nowDate = new Date();
        //订单过期时间 单位： 秒
        if(rq.getExpiredTime() != null){
            payOrder.setExpiredTime(DateUtil.offsetMinute(nowDate, rq.getExpiredTime()));
        }else{
            payOrder.setExpiredTime(DateUtil.offsetMinute(nowDate, 15));
        }
        payOrder.setCreatedAt(nowDate);
        if(StringUtils.isNotEmpty(rq.getRouteNo())){
            payOrder.setRouteId(rq.getRouteNo());
        }
        if(manage != null && manage.getRouteRecord() != null){
            manage.setStoreId(payOrder.getStoreId());
            manage.setApplyId(payOrder.getMchExtNo());
            routeRecordService.save(manage.getRouteRecord());
        }
        return payOrder;
    }

    /**
     * 获取H5支付的url
     * @param order
     * @return
     */
    public String getH5payUrl(PayOrder order) {
        String tk = sysConfigService.getDBApplicationConfig().genQrToken(QRCodeParams.TYPE_PAY_ORDER,order.getPayOrderId(),order.getFindAmt());
        String extParams = "&price="+ AmountUtil.convertCent2Dollar(order.getFindAmt()) + "&order_no="+ order.getPayOrderId() + "&env=h5";
        String payUrl = sysConfigService.getDBApplicationConfig().getPaySiteUrl() + "/pages/payment/index?"+JeepayKit.TOKEN_KEY+"="+tk;
        if(CS.PAY_WAY_CODE_TYPE.ALIPAY.equals(order.getWayCodeType())){
            AlipayOauth2Params oauth2Params = (AlipayOauth2Params) configContextQueryService.queryIsvOauth2Params(order.getIsvNo(), CS.IF_CODE.ALIPAY);
            payUrl = ALIPAY_START_APP_URL + URLUtil.encodeAll(SCHEME + URLUtil.encodeAll(payUrl + "&ap="+ oauth2Params.getAppId() + extParams));
        }else{
            payUrl = WxpayOauth2Params.WX_TG_PRIFEX + tk;
        }
        order.setTk(tk);
        return payUrl;
    }

    /**
     * 获取app支付参数
     * @param order
     * @return
     */
    public AppBizResp getAppPayData(PayOrder order) {
        String payUrl = getH5payUrl(order);
        ChannelAppletPayMsg liteInfo = loadLitePayData(order);
        AppBizResp resp = new AppBizResp(order,payUrl,liteInfo);
        return resp;
    }

    public ChannelAppletPayMsg loadLitePayData(PayOrder order){
        if(StringUtils.isEmpty(order.getTk())){
            String tk = sysConfigService.getDBApplicationConfig().genQrToken(QRCodeParams.TYPE_PAY_ORDER,order.getPayOrderId(),order.getFindAmt());
            order.setTk(tk);
        }
        String path = "pages/payment/index";
        String query = JeepayKit.TOKEN_KEY+"="+order.getTk() + "&price="+ AmountUtil.convertCent2Dollar(order.getFindAmt()) + "&order_no="+ order.getPayOrderId() + "&env=xcx";
        ChannelAppletPayMsg liteInfo = new ChannelAppletPayMsg();
        if(CS.PAY_WAY_CODE_TYPE.WECHAT.equals(order.getWayCodeType())){
            WxpayOauth2Params oauth2Params = (WxpayOauth2Params) configContextQueryService.queryIsvOauth2Params(order.getIsvNo(), CS.IF_CODE.WXPAY);
            if(oauth2Params != null && StringUtils.isNotEmpty(oauth2Params.getLiteAppId())){
                liteInfo = new ChannelAppletPayMsg(oauth2Params.getLiteAppId(),path + "?" + query,oauth2Params.getLiteGhid(),oauth2Params.getLiteEnv());
            }
        }else{
            AlipayOauth2Params oauth2Params = (AlipayOauth2Params) configContextQueryService.queryIsvOauth2Params(order.getIsvNo(), CS.IF_CODE.ALIPAY);
            String scheme = String.format("alipays://platformapi/startapp?appId=%s", oauth2Params.getLiteParams().getAppId());
            if(oauth2Params != null && oauth2Params.getLiteParams() != null){
                String payUrl = ALIPAY_START_APP_URL + URLUtil.encodeAll(scheme + "&page=" + path + "&query=" +  URLUtil.encodeAll(query));
                liteInfo = new ChannelAppletPayMsg(payUrl);
            }
        }
        return liteInfo;
    }

    /**
     * 获取扫码支付链接
     * @return
     */
    public String getScanPayUrl(PayOrder order) {
        DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();
        String tk = dbApplicationConfig.genQrToken(QRCodeParams.TYPE_PAY_ORDER,order.getPayOrderId(),order.getFindAmt());
        return dbApplicationConfig.getPaySiteUrl() + "/api/scan/imgs/" + tk + ".png";
    }
}
