package com.liquidnet.service.platform.utils;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.liquidnet.common.cache.redis.util.RedisDataSourceUtil;
import com.liquidnet.common.sms.constant.SmsEnum;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.base.SmsMessage;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.base.constant.RedisKeyExpireConst;
import com.liquidnet.service.kylin.constant.GroupBuyStatusConst;
import com.liquidnet.service.kylin.constant.KylinRedisConst;
import com.liquidnet.service.kylin.dto.vo.groupbuy.KylinGroupBuyMemberVo;
import com.liquidnet.service.kylin.dto.vo.groupbuy.KylinGroupBuyOrderVo;
import com.liquidnet.service.kylin.dto.vo.mongo.KylinOrderTicketVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 拼团失败 / 1h 提醒短信：入队 {@link MQConst.KylinQueue#SMS_NOTICE}（与购票短信一致，异步）。
 */
@Component
@Slf4j
public class GroupBuySmsSender {

    @Autowired
    private QueueUtils queueUtils;
    @Autowired
    private RedisDataSourceUtil redisDataSourceUtil;
    @Autowired
    private MongoTemplate mongoTemplate;

    public void notifyFailed(KylinGroupBuyOrderVo group) {
        if (group == null || !isSmsEnabled()) {
            return;
        }
        List<String> phones = collectPhones(group, true, false);
        if (phones.isEmpty()) {
            log.info("拼团失败短信无有效手机号 groupOrderId={}", group.getGroupOrderId());
            return;
        }
        ObjectNode params = JsonUtils.OM().createObjectNode();
        params.put("hours", resolveHours(group));
        for (String phone : phones) {
            enqueueSms(phone, SmsEnum.ADTemplate.SMS_510615071.name(), params,
                    group.getGroupOrderId(), "failed");
        }
    }

    public void notifyRemind1h(KylinGroupBuyOrderVo group) {
        if (group == null || !isSmsEnabled()) {
            return;
        }
        List<String> phones = collectPhones(group, false, true);
        if (phones.isEmpty()) {
            log.info("拼团1h提醒短信无有效手机号 groupOrderId={}", group.getGroupOrderId());
            return;
        }
        ObjectNode empty = JsonUtils.OM().createObjectNode();
        for (String phone : phones) {
            enqueueSms(phone, SmsEnum.ADTemplate.SMS_510285138.name(), empty,
                    group.getGroupOrderId(), "remind1h");
        }
        redisDataSourceUtil.getRedisKylinUtil().set(KylinRedisConst.GROUP_BUY_REMIND_1H.concat(group.getGroupOrderId()), "1",
                RedisKeyExpireConst.KYLIN_GROUP_BUY_EXPIRE);
    }

    public boolean isRemind1hAlreadySent(KylinGroupBuyOrderVo group) {
        if (group == null) {
            return true;
        }
        if (Integer.valueOf(1).equals(group.getRemind1hSent())) {
            return true;
        }
        Object v = redisDataSourceUtil.getRedisKylinUtil().get(KylinRedisConst.GROUP_BUY_REMIND_1H.concat(group.getGroupOrderId()));
        return v != null && ("1".equals(String.valueOf(v)) || "true".equalsIgnoreCase(String.valueOf(v)));
    }

    private boolean isSmsEnabled() {
        Object v = redisDataSourceUtil.getRedisKylinUtil().get(KylinRedisConst.GROUP_BUY_SMS_ENABLED);
        if (v == null) {
            return true;
        }
        String s = String.valueOf(v).trim();
        return !("0".equals(s) || "false".equalsIgnoreCase(s));
    }

    private List<String> collectPhones(KylinGroupBuyOrderVo group, boolean joinedOnly, boolean includePendingPay) {
        Set<String> phones = new HashSet<>();
        if (group == null || CollectionUtils.isEmpty(group.getMembers())) {
            return new ArrayList<>();
        }
        for (KylinGroupBuyMemberVo m : group.getMembers()) {
            if (m == null || StringUtils.isBlank(m.getOrderTicketsId())) {
                continue;
            }
            if (m.getOrderTicketsId().startsWith("ROBOT_")) {
                continue;
            }
            Integer st = m.getStatus();
            boolean ok;
            if (joinedOnly) {
                ok = GroupBuyStatusConst.MemberStatus.JOINED.equals(st);
            } else {
                ok = GroupBuyStatusConst.MemberStatus.JOINED.equals(st)
                        || (includePendingPay && GroupBuyStatusConst.MemberStatus.PENDING_PAY.equals(st));
            }
            if (!ok) {
                continue;
            }
            String mobile = resolveMobile(m.getOrderTicketsId());
            if (StringUtils.isNotBlank(mobile)) {
                phones.add(mobile.trim());
            }
        }
        return new ArrayList<>(phones);
    }

    private String resolveMobile(String orderTicketsId) {
        Object cached = redisDataSourceUtil.getRedisKylinUtil().get(KylinRedisConst.ORDER.concat(orderTicketsId));
        if (cached instanceof KylinOrderTicketVo) {
            return ((KylinOrderTicketVo) cached).getUserMobile();
        }
        KylinOrderTicketVo ticket = mongoTemplate.findOne(
                Query.query(Criteria.where("orderTicketsId").is(orderTicketsId)),
                KylinOrderTicketVo.class, KylinOrderTicketVo.class.getSimpleName());
        return ticket == null ? null : ticket.getUserMobile();
    }

    private String resolveHours(KylinGroupBuyOrderVo group) {
        try {
            if (StringUtils.isNotBlank(group.getCreatedAt()) && StringUtils.isNotBlank(group.getExpiresAt())) {
                LocalDateTime created = DateUtil.Formatter.yyyyMMddHHmmss.parse(group.getCreatedAt());
                LocalDateTime expires = DateUtil.Formatter.yyyyMMddHHmmss.parse(group.getExpiresAt());
                long hours = Duration.between(created, expires).toHours();
                if (hours < 1) {
                    hours = 1;
                }
                return String.valueOf(hours);
            }
        } catch (Exception e) {
            log.warn("拼团失败短信 hours 解析失败 groupOrderId={}", group.getGroupOrderId(), e);
        }
        return "24";
    }

    private void enqueueSms(String phone, String templateCode, ObjectNode params, String groupOrderId, String scene) {
        try {
            SmsMessage msg = SmsMessage.builder()
                    .setPhone(phone)
                    .setSignName(SmsEnum.ADSignName.M02.getVal())
                    .setTemplateCode(templateCode);
            if (params == null || params.size() == 0) {
                msg.setEmptyTemplateParam();
            } else {
                Iterator<Map.Entry<String, JsonNode>> it = params.fields();
                while (it.hasNext()) {
                    Map.Entry<String, JsonNode> e = it.next();
                    msg.setTemplateParam(e.getKey(), e.getValue() == null ? "" : e.getValue().asText(""));
                }
            }
            queueUtils.sendMsgByRedis(MQConst.KylinQueue.SMS_NOTICE.getKey(), msg.toJson());
        } catch (Exception e) {
            log.error("拼团短信入队异常 scene={} groupOrderId={} phone={}", scene, groupOrderId, phone, e);
        }
    }
}
