package com.jeequan.jeepay.thirdparty.device.printer.zgwlPrinter;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.device.IPrinterService;
import com.jeequan.jeepay.core.model.device.DeviceParams;
import com.jeequan.jeepay.core.model.device.PayOrderInfo4Device;
import com.jeequan.jeepay.core.model.device.ZgwlParams;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.thirdparty.device.speaker.zgwlSpeaker.ZgwlSpeakerService;
import com.jeequan.jeepay.thirdparty.device.speaker.zgwlSpeaker.util.ConnectionOptionWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.stereotype.Service;

/*
* 云打印厂商： 智谷物联
*
* @author zhuxiao
*
* @date 2021/6/8 18:10
*/
@Slf4j
@Service
public class ZgwlPrinterService implements IPrinterService {

    @Override
    public void send(JSONObject deviceParams, PayOrderInfo4Device printPayOrderInfo) {

        try {
            // 智谷物联参数
            ZgwlParams zgwlParams = getZgwlParams(deviceParams);

            // 消息体
            byte[] message = createMessage(zgwlParams, printPayOrderInfo);
            log.info("设备topic：{}，消息：{}", zgwlParams.getProviderParams().getTopic(), message);

            String clientId = zgwlParams.getProviderParams().getGroupId() + "@@@" + zgwlParams.getDeviceParams().getDeviceNo();

            // 发送消息
            sendMessage(zgwlParams.getProviderParams().getInstanceId(), zgwlParams.getProviderParams().getEndPoint(), zgwlParams.getProviderParams().getAccessKeyId(),
                    zgwlParams.getProviderParams().getAccessKeySecret(), clientId, zgwlParams.getProviderParams().getTopic(), message);

        }catch (Exception e) {
            log.error("智谷物联推送失败", e);
            throw new BizException(e.getMessage());
        }
    }

    @Override
    public void sendCustomMsg(JSONObject deviceParams, PayOrderInfo4Device payOrderInfo) throws BizException {

        ZgwlParams zgwlParams = getZgwlParams(deviceParams);

        // 设置播报、打印并播报才执行自定义播报
        if (zgwlParams.getBizConfigParams().getPrintMode() == DeviceParams.MODE_SPEAK || zgwlParams.getBizConfigParams().getPrintMode() == DeviceParams.MODE_PRINT_AND_SPEAK) {
            ZgwlSpeakerService speakerService = SpringBeansUtil.getBean(ZgwlSpeakerService.class);
            speakerService.sendCustomMsg(deviceParams, payOrderInfo);
        }else {
            throw new BizException("打印机未设置播报模式");
        }
    }

    @Override
    public String addPrinter(JSONObject deviceParams) {
        return null;
    }

    @Override
    public String editPrinter(JSONObject deviceParams) {
        return null;
    }

    @Override
    public String clearPrinter(JSONObject deviceParams) {
        return null;
    }


    /**
     *  根据订单  生成云打印机播报内容
     *  支付播报格式： 云打印机设备号|1007|交易单号|金额（元）|支付类型
     *  支付类型定义：2000-收款，2001-支付宝，2002-QQ，2003-微信，2004-京东，2005-银联，支持7571-7579自定义（自定义需要同时写入设备，如：7571-会员卡）
     * */
    public byte[] createMessage(ZgwlParams zgwlParams, PayOrderInfo4Device printPayOrderInfo) {

        //支付方式
        int payTypeCode = getPayTypeCode(printPayOrderInfo.getIfCode());

        String amount = ZgwlUtil.covertMoney(printPayOrderInfo.getAmount());

        //打印文本
        String printText = "订单号：" + printPayOrderInfo.getPayOrderId();
        printText += "\r\n订单金额：" + amount;
        printText += "\r\n下单时间：" + DateUtil.format(printPayOrderInfo.getCreatedAt(), DatePattern.NORM_DATETIME_FORMAT);
        printText +=  "\r\n\r\n\r\n\r\n\r\n";

        // 打印联数
        int printNum = zgwlParams.getBizConfigParams().getPrintNum() != null ? zgwlParams.getBizConfigParams().getPrintNum() : 1;
        if (zgwlParams.getBizConfigParams().getPrintMode() == DeviceParams.MODE_PRINT) {

            return ZgwlUtil.getPrinterBytes(printText, printNum, "");

        }else if (zgwlParams.getBizConfigParams().getPrintMode() == DeviceParams.MODE_SPEAK) {

            return ZgwlUtil.getStaticVoiceBytes(zgwlParams.getDeviceParams().getDeviceNo(), printPayOrderInfo.getPayOrderId(), amount, payTypeCode);

        }else {

            return ZgwlUtil.getPrinterVoiceBytes(printText, printNum, "" , zgwlParams.getDeviceParams().getDeviceNo(),
                    printPayOrderInfo.getPayOrderId(), amount, payTypeCode);
        }

    }


    /**
     * 发送消息
     */
    public void sendMessage(String instanceId, String endPoint, String accessKey, String secretKey, String clientId,
                            String parentTopic, byte[] message) throws Exception {

        /**
         * QoS参数代表传输质量，根据设备对接协议设定
         */
        final int qosLevel = 0;
        //发送端ClientID，请勿与TOPIC中ClientID重复，
        String masterClientId= clientId + StrUtil.subSufByLength(clientId, 6);

        ConnectionOptionWrapper connectionOptionWrapper = new ConnectionOptionWrapper(instanceId, accessKey, secretKey, masterClientId);
        final MemoryPersistence memoryPersistence = new MemoryPersistence();
        /**
         * 客户端使用的协议和端口必须匹配，具体参考文档 https://help.aliyun.com/document_detail/44866.html?spm=a2c4g.11186623.6.552.25302386RcuYFB
         */
        final MqttClient mqttClient = new MqttClient("tcp://" + endPoint + ":1883", masterClientId, memoryPersistence);
        /**
         * 客户端设置好发送超时时间，防止无限阻塞
         */
        mqttClient.setTimeToWait(5000);
        mqttClient.setCallback(new MqttCallbackExtended() {
            @Override
            public void connectComplete(boolean reconnect, String serverURI) {
                /**
                 * 客户端连接成功后就需要尽快订阅需要的 topic
                 */
                log.info("Mqtt connect success");
            }
            @Override
            public void connectionLost(Throwable throwable) {
                log.info("Mqtt connect lost");
                throwable.printStackTrace();
            }
            @Override
            public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
                /**
                 * 消费消息的回调接口，需要确保该接口不抛异常，该接口运行返回即代表消息消费成功。
                 * 消费消息需要保证在规定时间内完成，如果消费耗时超过服务端约定的超时时间，对于可靠传输的模式，服务端可能会重试推送，业务需要做好幂等去重处理。超时时间约定参考限制
                 * https://help.aliyun.com/document_detail/63620.html?spm=a2c4g.11186623.6.546.229f1f6ago55Fj
                 */

                log.info("receive msg from topic {} , body is {}", s, new String(mqttMessage.getPayload()));
            }
            @Override
            public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
                log.info("send msg succeed topic is : {}", iMqttDeliveryToken.getTopics()[0]);
            }
        });
        mqttClient.connect(connectionOptionWrapper.getMqttConnectOptions());
        /**
         * MQ4IoT支持点对点消息，点对点消息不需要经过订阅关系匹配。点对点消息的 topic 格式是  {{parentTopic}}/p2p/{{targetClientId}}
         */
        final String p2pSendTopic = parentTopic + "/p2p/" + clientId;
        MqttMessage mqttMessage = new MqttMessage(message);
        mqttMessage.setQos(qosLevel);
        mqttClient.publish(p2pSendTopic, mqttMessage);
    }

    private ZgwlParams getZgwlParams(JSONObject deviceParams){

        JSONObject bsjJSON = new JSONObject();
        bsjJSON.put("providerParams", JSON.parseObject(deviceParams.getString("providerParams")));
        bsjJSON.put("deviceParams", JSON.parseObject(deviceParams.getString("deviceParams")));
        bsjJSON.put("bizConfigParams", JSON.parseObject(deviceParams.getString("bizConfigParams")));

        // 智谷物联云喇叭参数
        return JSON.parseObject(bsjJSON.toJSONString(), ZgwlParams.class);
    }

    /**
     * 支付方式
     */
    private int getPayTypeCode(String ifCode) {
        if (StringUtils.isBlank(ifCode)) {
            return 2000;
        }

        if (CS.IF_CODE.WXPAY.equals(ifCode)) {
            return 2003;
        }else if (CS.IF_CODE.ALIPAY.equals(ifCode)) {
            return 2001;
        }else if (CS.IF_CODE.YSFPAY.equals(ifCode)) {
            return 2005;
        }else {
            return 2000;
        }
    }

}
