package com.jeequan.jeepay.pay.ctrl.webcashier;

import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentParser;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.*;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRS;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.QrCashierOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.QrCashierOrderRS;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.core.utils.DateKit;
import com.jeequan.jeepay.core.utils.JeepayKit;
import com.jeequan.jeepay.core.utils.JsonKit;
import com.jeequan.jeepay.db.entity.MchAppEntity;
import com.jeequan.jeepay.db.entity.MchConfig;
import com.jeequan.jeepay.db.entity.MchPayPassage;
import com.jeequan.jeepay.db.entity.PayOrder;
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
import com.jeequan.jeepay.pay.service.PayMchNotifyService;
import com.jeequan.jeepay.service.impl.*;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* WEB收银台controller
*
* @author terrfly
*
* @date 2022/7/1 17:27
*/
@Controller
@RequestMapping("/api/webCashier")
public class WebCashierController extends AbstractPayOrderController {

    @Autowired private PayOrderService payOrderService;
    @Autowired private SysConfigService sysConfigService;
    @Autowired private PayMchNotifyService payMchNotifyService;
    @Autowired private MchAppService mchAppService;
    @Autowired private MchPayPassageService mchPayPassageService;
    @Autowired private MchConfigService mchConfigService;
    @Autowired private ConfigContextQueryService configContextQueryService;

    /** 跳转到页面 **/
    @GetMapping("/{payOrderToken}")
    public String toPage(@PathVariable("payOrderToken") String payOrderToken){

        try {
            PayOrder payOrder = getPayOrder(payOrderToken);
            request.setAttribute("payOrder", payOrder);
            request.setAttribute("payOrderToken", payOrderToken);
            request.setAttribute("payAmount", AmountUtil.convertCent2Dollar(payOrder.getAmount()));

            // 查询商户收银台配置信息
            CashierConfig cashierConfig = mchConfigService.queryMchCashierConfigByDefault(payOrder.getMchNo(), MchConfig.WEB_CASHIER_GROUP_KEY);

            if(cashierConfig.getState() != CS.YES){
                throw new BizException("商户未开通[web收银台]功能");
            }


            DBOEMConfig oemConfig = sysConfigService.getOemConfig();

            // 静态CDN地址
            setPublicCdnHost();

            // 得到所有的支付方式
            Set<String> allWaycode = new HashSet<>();
            mchPayPassageService.list(MchPayPassage.gw().eq(MchPayPassage::getMchNo, payOrder.getMchNo()).eq(MchPayPassage::getAppId, payOrder.getAppId()).eq(MchPayPassage::getState, CS.YES))
                    .stream().forEach( r -> allWaycode.add(r.getWayCode()));

            // 处理 wxH5支付方式 and aliwap支付方式。
            cashierConfig.processWacodesWithWxH5AndALiwap(allWaycode);

            // 放置jsAPI
            if(allWaycode.contains(CS.PAY_WAY_CODE.WX_JSAPI) || allWaycode.contains(CS.PAY_WAY_CODE.ALI_JSAPI) || allWaycode.contains(CS.PAY_WAY_CODE.YSF_JSAPI) ){
                allWaycode.add(CS.PAY_WAY_CODE.QR_CASHIER);
            }

            request.setAttribute("allWaycode", JSON.toJSONString(allWaycode));

            request.setAttribute("oemConfig", oemConfig);

            request.setAttribute("cashierConfig", cashierConfig);

            // 倒计时 时间， 单位： s (不存在负值的情况)
            long downSecond = (payOrder.getExpiredTime().getTime() - DateKit.currentTimeMillis()) / 1000;
            request.setAttribute("downSecond",   downSecond < 0 ? 0L : downSecond);

            UserAgent userAgent = UserAgentParser.parse(request.getHeader("User-Agent"));
            if(userAgent.getPlatform().isMobile()){
                return "webcashier/webCashierH5";
            }
            return "webcashier/webCashier";

        } catch (BizException e) {
            logger.error("业务异常", e);
            request.setAttribute("errMsg", e.getMessage());
            return "webcashier/error";

        } catch (Exception e) {
            logger.error("异常", e);
            request.setAttribute("errMsg", e.getMessage());
            return "webcashier/error";
        }
    }

    /*  跳转到支付成功页面 **/
    @GetMapping("/paySuccess")
    public String toReturnSuccessPage(){

        // 静态CDN地址
        setPublicCdnHost();
        return "cashier/returnPage";
    }

    /** 支付方式的转换 **/
    @PostMapping ("/convertPayway/{payOrderToken}")
    @ResponseBody
    public ApiRes convertPayway(@PathVariable("payOrderToken") String payOrderToken){

        // 需要转换的支付方式
        String wayCode = getValStringRequired("wayCode");

        // 需要的方式（url跳转 or 二维码展示）
        String payDataType = getValString("payDataType");

        PayOrder payOrder = getPayOrder(payOrderToken);
        String payOrderId = payOrder.getPayOrderId();

        DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();

        MchAppEntity mchAppEntity = mchAppService.getById(payOrder.getAppId());
        List<String> appSignType = JSON.parseArray(mchAppEntity.getAppSignType(), String.class);

        // 聚合扫码
        if(CS.PAY_WAY_CODE.QR_CASHIER.equals(wayCode)){

            boolean updateflag = payOrderService.update(new LambdaUpdateWrapper<PayOrder>()
                    .set(PayOrder::getWayCode, CS.PAY_WAY_CODE.QR_CASHIER)
                    .eq(PayOrder::getPayOrderId, payOrder.getPayOrderId()).eq(PayOrder::getState, PayOrder.STATE_INIT));

            if(!updateflag){
                throw new BizException("更新订单状态异常");
            }

            QrCashierOrderRS result = new QrCashierOrderRS();
            String payUrl = dbApplicationConfig.genUniJsapiPayUrl(QRCodeParams.TYPE_PAY_ORDER, QRCodeParams.ENTRY_PAGE_H5, payOrderId, payOrder.getIsvNo());

            // 需要二维码参数
            if(CS.PAY_DATA_TYPE.CODE_IMG_URL.equals(payDataType)){
                result.setCodeImgUrl(dbApplicationConfig.genScanImgUrl(payUrl));
            }else{
                result.setPayUrl(payUrl);
            }

            result.setPayDataType(result.buildPayDataType());
            result.setPayData(result.buildPayData());
            return ApiRes.ok(result);

        }else{ // 转换为： 其他的支付方式类型


            // 判断是否 需要  h5 ==》 小程序

            CashierConfig cashierConfig = mchConfigService.queryMchCashierConfigByDefault(payOrder.getMchNo(), MchConfig.WEB_CASHIER_GROUP_KEY);

            // 得到所有的支付方式
            Set<String> allWaycode = new HashSet<>();
            mchPayPassageService.list(MchPayPassage.gw().select(MchPayPassage::getWayCode)
                    .eq(MchPayPassage::getMchNo, mchAppEntity.getMchNo()).eq(MchPayPassage::getAppId, mchAppEntity.getAppId()).eq(MchPayPassage::getState, CS.YES)).forEach(r -> allWaycode.add(r.getWayCode()));

            // 商户可用 < 合并 > 配置
            List<String> wxH5PaywayListWithIntersectionDistinct = cashierConfig.getWxH5PaywayListWithIntersectionDistinct(allWaycode);
            List<String> aliWapPaywayListWithIntersectionDistinct = cashierConfig.getAliWapPaywayListWithIntersectionDistinct(allWaycode);

            // 微信H5支付方式  &&  配置的支付方式首先类型并不是 微信H5 , 那么需要做转换。
            if(CS.PAY_WAY_CODE.WX_H5.equals(wayCode) && !wxH5PaywayListWithIntersectionDistinct.isEmpty() && !wxH5PaywayListWithIntersectionDistinct.get(0).equals(CS.PAY_WAY_CODE.WX_H5)){

                MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(mchAppEntity.getMchNo(), mchAppEntity.getAppId(),payOrder.getMchExtNo());

                QrCashierOrderRQ bizRQ = new QrCashierOrderRQ();
                bizRQ.setSignType(appSignType.get(0)); // 任意一种签名方式
                bizRQ.setMchNo(payOrder.getMchNo()); //
                bizRQ.setAppId(payOrder.getAppId()); //

                // 微信小程序URL
                bizRQ.setEntryLiteType(QRCodeParams.ENTRY_LITE_WXH5);

                ApiRes apiRes = qrCashierPayWay(bizRQ, mchAppConfigContext, payOrderId,null);
                return commonAutoBuildRes(apiRes);

            }

            // 支付宝wap 支付方式  &&  配置的支付方式首先类型并不是 支付宝wap , 那么需要做转换。
            if(CS.PAY_WAY_CODE.ALI_WAP.equals(wayCode) && !aliWapPaywayListWithIntersectionDistinct.isEmpty() && !aliWapPaywayListWithIntersectionDistinct.get(0).equals(CS.PAY_WAY_CODE.ALI_WAP)){

                MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(mchAppEntity.getMchNo(), mchAppEntity.getAppId(),payOrder.getMchExtNo());

                QrCashierOrderRQ bizRQ = new QrCashierOrderRQ();
                bizRQ.setSignType(appSignType.get(0)); // 任意一种签名方式
                bizRQ.setMchNo(payOrder.getMchNo()); //
                bizRQ.setAppId(payOrder.getAppId()); //

                // 生活号的形式
                if(aliWapPaywayListWithIntersectionDistinct.get(0).equals(CS.PAY_WAY_CODE.ALI_JSAPI)){

                    bizRQ.setEntryLiteType(QRCodeParams.ENTRY_LITE_ALIJSAPIH5);

                }else{ // 支付宝小程序URL

                    bizRQ.setEntryLiteType(QRCodeParams.ENTRY_LITE_ALIH5);
                }

                ApiRes apiRes = this.qrCashierPayWay(bizRQ, mchAppConfigContext,payOrderId,null);
                return commonAutoBuildRes(apiRes);

            }

            // 明确支付方式的：  下单：
            UnifiedOrderRQ rq = new UnifiedOrderRQ();
            if(StringUtils.isNotBlank(payDataType)){ // payDataType
                rq.setChannelExtra(JsonKit.newJson("payDataType", payDataType).toString());
            }

            rq.setSignType(appSignType.get(0)); // 任意一种签名方式
            rq.setWayCode(wayCode);
            ApiRes apiRes = this.unifiedOrder(wayCode, rq.buildBizRQ(), payOrder,null);
            return commonAutoBuildRes(apiRes);

        }

    }


    /** 查单 **/
    @PostMapping("orderState/{payOrderToken}")
    @ResponseBody
    public ApiRes orderState(@PathVariable("payOrderToken") String payOrderToken){

        String payOrderId = JeepayKit.aesDecode(payOrderToken);
        PayOrder payOrder = payOrderService.getById(payOrderId);

        JSONObject result = new JSONObject();
        result.put("state", payOrder.getState());

        // 支付成功
        if(payOrder.getState() == PayOrder.STATE_SUCCESS){

            // 包含同步回调地址
            if(StringUtils.isNotBlank(payOrder.getReturnUrl())){
                MchAppEntity mchAppEntity = mchAppService.getById(payOrder.getAppId());
                result.put("returnUrl", payMchNotifyService.createReturnUrl(payOrder, mchAppEntity.getAppSecret()));

            }else{
                result.put("returnUrl", "/api/webCashier/paySuccess");
            }
        }

        return ApiRes.ok(result);
    }


    private PayOrder getPayOrder(String payOrderToken){

        String payOrderId = JeepayKit.aesDecode(payOrderToken);
        PayOrder payOrder = payOrderService.getById(payOrderId);

        if(payOrder == null || payOrder.getState() != PayOrder.STATE_INIT){
            throw new BizException("收银台订单不存在或状态不正确");
        }

        return payOrder;
    }


    /** 统一处理 buildPayData （ UnifiedOrderRS ） **/
    private ApiRes commonAutoBuildRes(ApiRes apiRes){

        UnifiedOrderRS bizRes = (UnifiedOrderRS)apiRes.getData();
        if(bizRes == null){
            return apiRes;
        }
        bizRes.setPayDataType(bizRes.buildPayDataType());
        bizRes.setPayData(bizRes.buildPayData());
        return ApiRes.ok(bizRes);
    }

    public void setPublicCdnHost(){
        // 静态CDN地址
        DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();
        request.setAttribute("staticCdnHost", dbApplicationConfig.getStaticCdnHost());
    }

}
