/**
 * cljpay.com
 * Copyright (C) 2015-2023 All Rights Reserved.
 */package com.jeequan.jeepay.pay.ctrl.zw;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.ServiceException;
import com.jeequan.jeepay.core.constants.ApiCodeEnum;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.MchApp;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.ApiRes;
import com.jeequan.jeepay.core.model.device.ZwParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.payorder.QueryPayOrderRS;
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.AutoBarOrderRQ;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.core.utils.JeepayKit;
import com.jeequan.jeepay.core.utils.SeqKit;
import com.jeequan.jeepay.db.entity.*;
import com.jeequan.jeepay.model.DeviceInfo;
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
import com.jeequan.jeepay.pay.service.ChannelOrderReissueService;
import com.jeequan.jeepay.service.impl.*;
import com.jeequan.jeepay.thirdparty.service.zw.model.request.PrintPdRequest;
import com.jeequan.jeepay.thirdparty.service.zw.model.request.UpdatePayParamRequest;
import com.jeequan.jeepay.thirdparty.service.zw.model.request.ZwPayParam;
import com.jeequan.jeepay.thirdparty.service.zw.model.request.ZwPayQueryParam;
import com.jeequan.jeepay.thirdparty.service.zw.uils.SignatureUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * @author yangjun
 * @version ZwController.java, v 0.1 2023-02-21 09:58 yangjun
 */
@Slf4j
@RestController
@RequestMapping("/api/zw")
public class ZwController extends AbstractPayOrderController {

    @Autowired
    private MchStoreDeviceService mchStoreDeviceService;
    @Autowired
    private MchInfoService mchInfoService;
    @Autowired
    private DeviceProvideConfigService deviceProvideConfigService;
    @Autowired
    private MchStoreService mchStoreService;
    @Autowired
    private MchAppService mchAppService;
    @Autowired
    private PayWayService payWayService;
    @Autowired
    private PayOrderService payOrderService;
    @Autowired
    private ChannelOrderReissueService channelOrderReissueService;

    /**
     * 支付 校验数据 + 验签
     *
     * @param reqString
     * @param zwPayParam
     * @return
     */
    private Object checkParamsWithSign(String reqString, ZwPayParam zwPayParam) {
        String deviceId = zwPayParam.getSn();
        if (StringUtils.isBlank(deviceId)) {
            return "设备sn不能为空";
        }
        String sign = zwPayParam.getMd5();
        if (StringUtils.isBlank(sign)) {
            return "签名参数不能为空";
        }

        JSONObject respJson = JSONObject.parseObject(reqString);
        String termid = respJson.getString("termid");

        // 打印机设备信息
        MchStoreDevice mchStoreDevice = mchStoreDeviceService.getOne(MchStoreDevice.gw()
                .eq(MchStoreDevice::getDeviceType, MchStoreDevice.DEVICE_TYPE_PRINTER)
                .eq(MchStoreDevice::getDeviceNo, deviceId)
                .eq(MchStoreDevice::getProvider, termid)
        );
        if (mchStoreDevice == null) {
            return "该设备没有入库";
        } else if (mchStoreDevice.getState() != CS.YES) {
            return "该设备已停用";
        } else if (mchStoreDevice.getBindState() != CS.YES) {
            return "该设备没有绑定商户";
        }

        DeviceProvideConfig deviceProvideConfig = deviceProvideConfigService.getOne(DeviceProvideConfig.gw()
                .eq(DeviceProvideConfig::getConfigId, mchStoreDevice.getConfigId())
                .eq(DeviceProvideConfig::getDeviceType, MchStoreDevice.DEVICE_TYPE_PRINTER)
                .eq(DeviceProvideConfig::getProvider, CS.DEVICE_PROVIDER.ZW));
        if (deviceProvideConfig == null) {
            return "设备厂商异常";
        }

        // 验签
        ZwParams.ProviderParams zwProviderParams = JSONObject.parseObject(deviceProvideConfig.getProviderParams(), ZwParams.ProviderParams.class);
        try {
            Boolean checkSign = SignatureUtil.checkIsSignValidFromResponseString(reqString, zwProviderParams.getMd5Key());
            if (!checkSign) {
                return "验签失败";
            }
        } catch (Exception ex) {
            log.error("ZwController >> checkParamsWithSign >> 智网打印机支付请求验签异常：deviceProvideConfig ={}", deviceProvideConfig, ex);
            return "验签异常";
        }


        String provider = deviceProvideConfig.getProvider();
        String mchNo = mchStoreDevice.getMchNo();

        MchInfo mchInfo = mchInfoService.getById(mchNo);
        if (mchInfo == null) {
            return "设备绑定商户不存在";
        } else if (mchInfo.getState() != CS.YES) {
            return "当前商户已停用";
        }

        String mchAppId = mchStoreDevice.getAppId();
        MchAppEntity mchAppEntity = mchAppService.getById(mchAppId);
        if (mchAppEntity == null) {
            return "设备绑定应用不存在";
        } else if (mchAppEntity.getState() != CS.YES) {
            return "设备绑定应用已停用";
        }

        String storeId = mchStoreDevice.getStoreId();
        MchStore mchStore = mchStoreService.getById(storeId);
        if (mchStore == null) {
            return "设备绑定门店不存在";
        }

        JSONObject deviceParamsJSON = new JSONObject();
        deviceParamsJSON.put("mchInfo", mchInfo);
        deviceParamsJSON.put("mchStore", mchStore);
        deviceParamsJSON.put("mchApp", mchAppEntity);
        deviceParamsJSON.put("provider", provider);
        deviceParamsJSON.put("deviceNo", mchStoreDevice.getDeviceNo());
        return deviceParamsJSON;
    }

    /**
     * 构建小票
     *
     * @param bizRes
     * @param deviceSn
     * @return
     */
    private List<PrintPdRequest> buildTicket(QueryPayOrderRS bizRes, String deviceSn) {
        // 判断设备支付小票打印权限
        MchStoreDevice dbRecord = mchStoreDeviceService.getOne(MchStoreDevice.gw()
                .eq(MchStoreDevice::getDeviceType, MchStoreDevice.DEVICE_TYPE_PRINTER)
                .eq(MchStoreDevice::getDeviceNo, deviceSn));
        UpdatePayParamRequest updatePayParamRequest = JSONObject.parseObject(dbRecord.getDeviceParams(), UpdatePayParamRequest.class);
        if (updatePayParamRequest != null && updatePayParamRequest.getBill_pay_print() != null && updatePayParamRequest.getBill_pay_print() == 0) {
            return null;
        }
        List<PrintPdRequest> printPdRequests = new ArrayList<>();
        PrintPdRequest printPdRequest = new PrintPdRequest();
        printPdRequest.setTp("3");
        printPdRequests.add(printPdRequest);

        printPdRequest = new PrintPdRequest();
        printPdRequest.setTp("1");
        printPdRequest.setCv("支付凭证");
        printPdRequest.setAl("1");
        printPdRequest.setFs("3");
        printPdRequest.setTb("1");
        printPdRequests.add(printPdRequest);

        printPdRequest = new PrintPdRequest();
        printPdRequest.setTp("4");
        printPdRequests.add(printPdRequest);

        printPdRequest = new PrintPdRequest();
        printPdRequest.setTp("1");
        printPdRequest.setCv("门店名称：" + bizRes.getStoreId());
        printPdRequests.add(printPdRequest);

        printPdRequest = new PrintPdRequest();
        printPdRequest.setTp("1");
        printPdRequest.setCv("订 单 号：" + bizRes.getPayOrderId());
        printPdRequests.add(printPdRequest);

        printPdRequest = new PrintPdRequest();
        printPdRequest.setTp("1");
        printPdRequest.setCv("商户单号：" + bizRes.getMchOrderNo());
        printPdRequests.add(printPdRequest);

        printPdRequest = new PrintPdRequest();
        printPdRequest.setTp("1");
        printPdRequest.setCv("支付状态：" + "");
        printPdRequests.add(printPdRequest);

        printPdRequest = new PrintPdRequest();
        printPdRequest.setTp("1");
        printPdRequest.setCv("支付方式：" + CS.PAY_WAY_CODE_TYPE_MAP.get(bizRes.getWayCodeType()));
        printPdRequests.add(printPdRequest);

        printPdRequest = new PrintPdRequest();
        printPdRequest.setTp("1");
        printPdRequest.setCv("下单时间：" + "");
        printPdRequests.add(printPdRequest);

        printPdRequest = new PrintPdRequest();
        printPdRequest.setTp("4");
        printPdRequests.add(printPdRequest);

        printPdRequest = new PrintPdRequest();
        printPdRequest.setTp("1");
        printPdRequest.setCv("完成时间：" + "");
        printPdRequests.add(printPdRequest);

        printPdRequest = new PrintPdRequest();
        printPdRequest.setTp("1");
        printPdRequest.setCv("交易金额：" + AmountUtil.convertCent2Dollar(bizRes.getAmount().toString()));
        printPdRequests.add(printPdRequest);

        return printPdRequests;
    }

    /**
     * 支付
     *
     * @param request
     * @return
     */
    @RequestMapping("/pay")
    public ZwPosRes pay(HttpServletRequest request) {

        String resultStr = getBody(request);
        log.info("ZwController >> pay >> 智网打印机支付请求：{}", resultStr);

        ZwPayParam zwPayParam = JSONObject.parseObject(resultStr, ZwPayParam.class);

        if (StringUtils.isBlank(zwPayParam.getAmount())) {
            return ZwPosRes.error(zwPayParam.getUuid(), "支付金额不能为空");
        }
        if (StringUtils.isBlank(zwPayParam.getPaycode())) {
            return ZwPosRes.error(zwPayParam.getUuid(), "条码不能为空");
        }

        // 验证通用请求字段、签名true
        Object object = checkParamsWithSign(resultStr, zwPayParam);
        if (object instanceof String) {
            log.info("ZwController >> pay >> 智网打印机支付,验证不过.error={}", object);
            return ZwPosRes.error(zwPayParam.getUuid(), (String) object);
        }
        JSONObject deviceJSON = (JSONObject) object;
        try {
            ApiRes apiRes = doPayNew(zwPayParam, deviceJSON);
            log.info("ZwController >> pay >> 下单返回参数.ApiRes={}", apiRes);
            if (apiRes.getCode().equals(ApiCodeEnum.CUSTOM_FAIL.getCode())) {
                return ZwPosRes.error(zwPayParam.getUuid(), apiRes.getMsg());
            }
            UnifiedOrderRS bizRs = (UnifiedOrderRS) apiRes.getData();
            log.info("ZwController >> pay >> 下单响应参数 bizRs = {}", JSON.toJSON(bizRs));
            // 支付小票
            if (bizRs.getOrderState() == PayOrder.STATE_SUCCESS) {
                ZwPosRes zwPosRes = ZwPosRes.success(zwPayParam.getUuid(), "支付成功");
                log.info("ZwController >> pay >> 智网打印机支付 zwPosRes = {}", JSON.toJSON(zwPosRes));
                return zwPosRes;
            } else if (bizRs.getOrderState() == PayOrder.STATE_FAIL
                    || bizRs.getOrderState() == PayOrder.STATE_CANCEL
                    || bizRs.getOrderState() == PayOrder.STATE_REFUND
                    || bizRs.getOrderState() == PayOrder.STATE_CLOSED
            ) {
                ZwPosRes zwPosRes = ZwPosRes.error(zwPayParam.getUuid(), bizRs.getErrCode() + bizRs.getErrMsg());
                log.info("ZwController >> pay >> 智网打印机支付 zwPosRes = {}", JSON.toJSON(zwPosRes));
                return zwPosRes;
            }
            ZwPosRes zwPosRes = ZwPosRes.ing(zwPayParam.getUuid(), "支付中");
            log.info("ZwController >> pay >> 智网打印机支付 zwPosRes = {}", JSON.toJSON(zwPosRes));
            return zwPosRes;
        } catch (BizException e) {
            log.error("PosController >> pos >> 打印机下单异常：", e);
            return ZwPosRes.error("StringPool.EMPTY", e.getMessage());
        }
    }

    /**
     * 支付查单
     **/
    @RequestMapping("/query")
    public ZwPosRes query(HttpServletRequest request) {
        String resultStr = getBody(request);
        ZwPayParam zwPayParam = JSONObject.parseObject(resultStr, ZwPayParam.class);

        log.info("ZwController >> pay >> 智网打印机支付查单请求：zwPayParam: {}", JSON.toJSON(zwPayParam));
        ZwPayQueryParam zwPayQueryParam = JSONObject.parseObject(resultStr, ZwPayQueryParam.class);
        if (StringUtils.isBlank(zwPayQueryParam.getUuid())) {
            return ZwPosRes.ing(zwPayQueryParam.getUuid(), "订单号不能为空");
        }

        // 验证通用请求字段、签名true
        Object object = checkParamsWithSign(resultStr, zwPayParam);
        if (object instanceof String) {
            return ZwPosRes.error(zwPayParam.getUuid(), (String) object);
        }
        JSONObject deviceJSON = (JSONObject) object;
        String merchantId = deviceJSON.getJSONObject("mchInfo").getString("mchNo");

        // 支持根据商户单号查询
        PayOrder payOrder = payOrderService.queryMchOrder(merchantId, null, zwPayQueryParam.getUuid());
        if (payOrder == null) {
            return ZwPosRes.ing(zwPayQueryParam.getUuid(), "订单信息错误");
        }

        // 支付中主查
        if (PayOrder.STATE_ING == payOrder.getState()) {
            ChannelRetMsg channelRetMsg = channelOrderReissueService.processPayOrder(payOrder);
            if (channelRetMsg != null && channelRetMsg.getChannelState() != null && channelRetMsg.getChannelState().equals(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS)) {
                return ZwPosRes.success(zwPayQueryParam.getUuid(), null);
            }
        }

        // 支付小票
        if (payOrder.getState() == PayOrder.STATE_SUCCESS) {
            List<PrintPdRequest> printPdRequests = buildTicket(QueryPayOrderRS.buildByPayOrder(payOrder), zwPayQueryParam.getSn());
            ZwPosRes zwPosRes = ZwPosRes.success(zwPayQueryParam.getUuid(), printPdRequests.toString());
            log.info("ZwController >> pay >> 智网打印机支付 zwPosRes = {}", JSON.toJSON(zwPosRes));
            return zwPosRes;
        } else if (payOrder.getState() == PayOrder.STATE_FAIL
                || payOrder.getState() == PayOrder.STATE_CANCEL
                || payOrder.getState() == PayOrder.STATE_REFUND
                || payOrder.getState() == PayOrder.STATE_CLOSED
        ) {
            return ZwPosRes.error(zwPayQueryParam.getUuid(), payOrder.getErrMsg());
        }
        return ZwPosRes.ing(zwPayQueryParam.getUuid(), "支付中");
    }


    /**
     * 智网打印机 支付优化
     *
     * @param zwPayParam
     * @param deviceJSON
     * @return
     */
    private ApiRes doPayNew(ZwPayParam zwPayParam, JSONObject deviceJSON) {
        // 下单参数
        String amount = AmountUtil.convertDollar2Cent(zwPayParam.getAmount());
        String wayCode = JeepayKit.getPayWayCodeByBarCode(zwPayParam.getPaycode());
        // 付款码
        String authCode = zwPayParam.getPaycode();

        MchAppEntity mchAppEntity = deviceJSON.getObject("mchApp", MchAppEntity.class);
        MchStore mchStore = deviceJSON.getObject("mchStore", MchStore.class);
        UnifiedOrderRQ unifiedOrderRQ = new UnifiedOrderRQ();
        // 商户号
        unifiedOrderRQ.setMchNo(mchAppEntity.getMchNo());
        unifiedOrderRQ.setAppId(mchAppEntity.getAppId());
        unifiedOrderRQ.setStoreId(mchStore.getStoreId());
        // 商户单号特殊处理
        String deviceOrderNum = zwPayParam.getUuid();
        if (StringUtils.isNotBlank(deviceOrderNum)) {
            unifiedOrderRQ.setMchOrderNo(deviceOrderNum);
        } else {
            unifiedOrderRQ.setMchOrderNo(SeqKit.genMhoOrderId());
        }
        unifiedOrderRQ.setWayCode(wayCode);
        unifiedOrderRQ.setAmount(Long.valueOf(amount));
        unifiedOrderRQ.setCurrency("CNY");
        unifiedOrderRQ.setClientIp(getClientIp());
        unifiedOrderRQ.setSubject("标题");
        unifiedOrderRQ.setBody("商品信息");
        unifiedOrderRQ.setSignType(MchApp.SIGN_TYPE_MD5);
        // DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();
        // 回调地址
        // unifiedOrderRQ.setNotifyUrl(dbApplicationConfig.getMchSiteUrl() + "/api/anon/paytestNotify/payOrder");
        // 设置扩展参数
        JSONObject extParams = new JSONObject();
        extParams.put("authCode", authCode.trim());
        unifiedOrderRQ.setChannelExtra(extParams.toString());
        // 设备信息
        DeviceInfo deviceInfo = new DeviceInfo();
        deviceInfo.setDeviceType(PayOrder.DEVICE_TYPE_PRINTER);
        deviceInfo.setDeviceNo(deviceJSON.getString("deviceNo"));
        deviceInfo.setProvider(deviceJSON.getString("provider"));
        unifiedOrderRQ.setDeviceInfo(JSON.toJSONString(deviceInfo));

        try {
            UnifiedOrderRQ bizRQ = buildBizRQ(unifiedOrderRQ);

            return this.unifiedOrder(bizRQ.getWayCode(), bizRQ);
        } catch (BizException e) {
            throw e;
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
    }


    /**
     * 统一下单
     *
     * @param rq
     * @return
     */
    public ApiRes unifiedOrder(UnifiedOrderRQ rq) {
        UnifiedOrderRQ bizRQ = buildBizRQ(rq);
        //实现子类的res
        ApiRes apiRes = unifiedOrder(bizRQ.getWayCode(), bizRQ);
        if (apiRes.getData() == null) {
            return apiRes;
        }
        UnifiedOrderRS bizRes = (UnifiedOrderRS) apiRes.getData();
        //聚合接口，返回的参数
        UnifiedOrderRS res = new UnifiedOrderRS();
        BeanUtils.copyProperties(bizRes, res);
        //只有 订单生成（QR_CASHIER） || 支付中 || 支付成功返回该数据
        if (bizRes.getOrderState() != null
                && (bizRes.getOrderState() == PayOrder.STATE_INIT
                || bizRes.getOrderState() == PayOrder.STATE_ING
                || bizRes.getOrderState() == PayOrder.STATE_SUCCESS)) {
            res.setPayDataType(bizRes.buildPayDataType());
            res.setPayData(bizRes.buildPayData());
        }
        return ApiRes.ok(res);
    }


    /**
     * 支付参数构建
     *
     * @param rq
     * @return
     */
    private UnifiedOrderRQ buildBizRQ(UnifiedOrderRQ rq) {
        //支付方式  比如： ali_bar
        String wayCode = rq.getWayCode();

        //jsapi 收银台聚合支付场景 (不校验是否存在payWayCode)
        if (CS.PAY_WAY_CODE.QR_CASHIER.equals(wayCode)) {
            return rq.buildBizRQ();
        }

        //web收银台聚合支付场景 (不校验是否存在payWayCode)
        if (CS.PAY_WAY_CODE.WEB_CASHIER.equals(wayCode)) {
            return rq.buildBizRQ();
        }

        //如果是自动分类条码
        if (CS.PAY_WAY_CODE.AUTO_BAR.equals(wayCode)) {

            AutoBarOrderRQ bizRQ = (AutoBarOrderRQ) rq.buildBizRQ();
            wayCode = JeepayKit.getPayWayCodeByBarCode(bizRQ.getAuthCode());
            rq.setWayCode(wayCode.trim());
        }

        if (payWayService.count(PayWay.gw().eq(PayWay::getWayCode, wayCode)) <= 0) {
            throw new BizException("不支持的支付方式");
        }

        //转换为 bizRQ
        return rq.buildBizRQ();
    }


    /**
     * 获取请求数据
     *
     * @param request
     * @return
     */
    protected final String getBody(HttpServletRequest request) {
        InputStreamReader in = null;
        try {
            in = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8);
            StringBuffer bf = new StringBuffer();
            int len;
            char[] chs = new char[1024];
            while ((len = in.read(chs)) != -1) {
                bf.append(new String(chs, 0, len));
            }
            return bf.toString();
        } catch (Exception e) {
            log.error("请求头部取数据异常：{}", e.getMessage());
            throw new ServiceException(e.getMessage());
        } finally {
            if (null != in) {
                try {
                    in.close();
                } catch (Exception e) {
                    log.error("流关闭异常:{}", e.getMessage());
                }
            }
        }
    }
}
