package com.jxy.marketall.task.publish;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.jxy.common.core.text.Convert;
import com.jxy.common.core.utils.DateUtils;
import com.jxy.common.core.utils.SpringUtils;
import com.jxy.common.core.utils.StringUtils;
import com.jxy.marketall.dto.DateBigDecimalDto;
import com.jxy.marketall.dto.SubscriptionContentDto;
import com.jxy.marketall.enums.SubscriptionType;
import com.jxy.marketall.service.MsgPushMailService;
import com.jxy.marketall.service.impl.MsgPushMaliServiceImpl;
import com.jxy.marketall.service.impl.MsgSubServiceImpl;
import com.jxy.system.api.RemoteMailService;
import com.jxy.system.api.entity.EmailEntity;
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Description 基于4419 btcdb dailyindsv2e1表的推送
 * @Date 2024/1/30 17:32
 * @Author 杜懿
 */
@Component
@Order(value =1)
public class BTCOnChainConsumer implements ApplicationRunner {

    @Autowired
    public static MsgSubServiceImpl msgSubService;

    public static MsgPushMaliServiceImpl mailService;
    //用于存储上一条数据的map
    public static Map<String, DateBigDecimalDto> preDataMap;

    public void run(ApplicationArguments args) throws Exception {
        msgSubService = SpringUtils.getBean("msgSubService");

        mailService = SpringUtils.getBean("msgPushMaliServiceImpl");

        //push消费者，由Broker推送消息
        try {
            pushConsumer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private static void pullConsumer() {
        //定义一个pull消费者
        DefaultMQPullConsumer consumer = new DefaultMQPullConsumer("consumer-group");

        //指定nameserver
        //本地
        //consumer.setNamesrvAddr("10.168.2.125:9876");
        //服务器
        consumer.setNamesrvAddr("54.254.93.204:9876");
        //指定消费的topic和tag
    }

    private static void pushConsumer() throws Exception {
        System.out.println("开始消费消息");

        //定义一个push消费者
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer-group");

        //指定nameserver
        //本地
        //consumer.setNamesrvAddr("10.168.2.125:9876");
        //服务器
        consumer.setNamesrvAddr("54.254.93.204:9876");

        // 指定从第一条消息开始消费，指定消费offset
        //consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);

        // 指定从最后一条消息开始消费(即无视历史记录)，指定消费offset
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);

        // 指定消费topic与tag *号表示全部tag
        //consumer.subscribe("BTC_dailyindsv2e1", "realTime");
        consumer.subscribe("BTC_v2e1_rt", "*");

        // 指定采用“广播模式”进行消费，默认为“集群模式”
         consumer.setMessageModel(MessageModel.BROADCASTING);
        System.out.println("tag111");
        // 注册消息监听器 ,
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            // 一旦broker中有了其订阅的消息就会触发该方法的执行，
            // 其返回值为当前consumer消费的状态
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {

                // 逐条消费消息
                for (MessageExt msg : msgs) {
                    System.out.println("开始逐条消费");
                    //System.out.println("btc test消费者收到消息"+ msg.getBody().toString());
                    String body = new String(msg.getBody(), StandardCharsets.UTF_8);
                    System.out.println(" 消费 body:"+body);
                    Map<String, DateBigDecimalDto> map = JSON.parseObject(body, new TypeReference<HashMap<String, DateBigDecimalDto>>(){});

                    System.out.println("map:"+map);
                    if(StringUtils.isNotNull(preDataMap)){
                        //根据时间大致判断当前数据是否异常
                        if(preDataMap.get("unixdt").getDate().compareTo(map.get("unixdt").getDate()) <0){
                            //当前数据时间小于前一条数据时间 数据异常 不执行后续代码
                            System.out.println("异常");
                            continue;
                        }
                    }

                    //查询用户订阅关系表中 订阅了此数据的所有数据，根据数据通知用户
                    //拿所有用户的订阅关系 便于后续根据
                    System.out.println("准备输出list");
                    List<SubscriptionContentDto> list =
                            msgSubService.getSubListByChannel(SubscriptionType.BTC_ACTIVE_ADDRESS_REALTIME.getChannel());
                    //遍历map 推送数据
                    System.out.println("输出list"+list);
                    Iterator<Map.Entry<String, DateBigDecimalDto>> iterator = map.entrySet().iterator();
                    System.out.println("iterator"+map.entrySet());
                    Map.Entry<String, DateBigDecimalDto> entry;
                    while (iterator.hasNext()){
                        System.out.println("开始便利");
                        entry = iterator.next();
                        String metricsId = entry.getKey();
                        System.out.println("entry:"+entry+",id:"+metricsId);
                        System.out.println("while list:"+list);
                        System.out.println("channel:"+SubscriptionType.getStatusById(metricsId).getChannel());
                        System.out.println("metrics"+SubscriptionType.getStatusById(metricsId).getMetrics());
                        System.out.println("map.get:"+map.get(metricsId));
                        if(StringUtils.isNull(preDataMap)){
                            System.out.println("preDataMap is null");
                            pushRealTimeDataToUser(list,SubscriptionType.getStatusById(metricsId).getChannel(),metricsId,null,map.get(metricsId));
                        }else {
                            pushRealTimeDataToUser(list,SubscriptionType.getStatusById(metricsId).getChannel(),metricsId,preDataMap.get(metricsId),map.get(metricsId));
                        }
                    }

                    //记录当前数据 便于与下一次msg的数据对比
                    preDataMap = map;

                }

                // 返回消费状态：消费成功
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }

        });
        // 开启消费者消费
        consumer.start();
        System.out.println("开始消费");

    }

    /**
     * 向指定用户推送数据
     * @return
     */
    public static boolean pushRealTimeDataToUser(List<SubscriptionContentDto> list, String channel, String metrics, DateBigDecimalDto preDto,DateBigDecimalDto dto){
        //从总数据list中赛选出指定频道(channel)下的指定指标 且订阅周期为realtime的用户订阅数据

        System.out.println("调用推送方法");
        List<SubscriptionContentDto> list1 = list.stream().filter(e -> e.getChannel().equals(channel) && e.getMetrics().equals(metrics) &&e.getCycle().toLowerCase().equals("realtime")).collect(Collectors.toList());
        System.out.println("筛选后的结果："+list1);
        list1.stream().forEach(e -> {
            if(StringUtils.isBlank(e.getContent())){
                //content或address为空表示有数据就直接推 此处只根据content 不根据address 地址单独用个接口处理推送业务
                //直接推送
                System.out.println("推送1");
                noticeUser(e,dto);
            }else {
                //根据operator+content处理
                //operator为>、<、<=、>=时才根据operator+content处理 其他情况默认只根据content的只处理
                //只根据content处理:content值可能为+value、value、-value  所以判断应该用preData+valueOfContent < nowData 如果结果为true表示当前数据相比前一条数据
                switch (e.getOperator()){
                    case ">":
                        if(dto.getValue().compareTo(Convert.toBigDecimal(e.getContent())) > 0){
                            System.out.println("推送>");
                            noticeUser(e,dto);
                        }
                        break;
                    case ">=":
                        if(dto.getValue().compareTo(Convert.toBigDecimal(e.getContent())) >= 0){
                            System.out.println("推送>=");
                            noticeUser(e,dto);
                        }
                        break;
                    case "<":
                        if(dto.getValue().compareTo(Convert.toBigDecimal(e.getContent())) < 0){
                            System.out.println("推送<");
                            noticeUser(e,dto);
                        }
                        break;
                    case "<=":
                        if(dto.getValue().compareTo(Convert.toBigDecimal(e.getContent())) <= 0){
                            System.out.println("推送<=");
                            noticeUser(e,dto);
                        }
                        break;
                    default:
                        //只根据content处理:content值可能为+value、value、-value  所以判断应该用preData+valueOfContent < nowData 如果结果为true表示当前数据相比前一条数据
                        if (StringUtils.isNotNull(preDto)){
                            //System.out.println("preDto is not null");
                            if(preDto.getValue().add(Convert.toBigDecimal(e.getContent(), BigDecimal.ZERO)).compareTo(dto.getValue()) <= 0){
                                //System.out.println("推送计算后的值");
                                noticeUser(e,dto);
                            }
                        }
                        break;
                }
            }

        });
        return true;
    }

    public static void noticeUser(SubscriptionContentDto subDto,DateBigDecimalDto dataDto){

        //System.out.println("进入noticeUser");
        String subject ="data push";
        String msg="您订阅的指标"+subDto.getChannel()+":"+SubscriptionType.getMetricsById(subDto.getMetrics())+"数据符合您的推送要求，数据详情:" +
                DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS,dataDto.getDate())+"(UTC时间),value:"+dataDto.getValue()+"";
        mailService.sendTextMailMessage(subDto.getEmail(),subject,msg);
        System.out.println("邮件已发送");

    }
}
