package com.liquidnet.service.kylin.utils;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.liquidnet.common.cache.redis.util.RedisUtil;
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.KylinGroupBuyActivityVo;
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.middle.KylinTicketTimesVo;
import com.liquidnet.service.kylin.dto.vo.middle.KylinTicketVo;
import com.liquidnet.service.kylin.dto.vo.mongo.KylinOrderTicketVo;
import com.liquidnet.service.kylin.dto.vo.mongo.KylinPerformanceVo;
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;

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

    @Autowired
    private QueueUtils queueUtils;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private DataUtils dataUtils;

    public void notifySuccess(KylinGroupBuyOrderVo group) {
        if (group == null || !isSmsEnabled()) {
            return;
        }
        List<String> phones = collectPhones(group, true, false);
        if (phones.isEmpty()) {
            return;
        }
        ObjectNode params = buildSuccessParams(group);
        for (String phone : phones) {
            enqueueSms(phone, SmsEnum.ADTemplate.SMS_510540063.name(), params,
                    group.getGroupOrderId(), "success");
        }
    }

    public void notifyFailed(KylinGroupBuyOrderVo group) {
        if (group == null || !isSmsEnabled()) {
            return;
        }
        List<String> phones = collectPhones(group, true, false);
        if (phones.isEmpty()) {
            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()) {
            return;
        }
        ObjectNode empty = JsonUtils.OM().createObjectNode();
        for (String phone : phones) {
            enqueueSms(phone, SmsEnum.ADTemplate.SMS_510285138.name(), empty,
                    group.getGroupOrderId(), "remind1h");
        }
        redisUtil.set(KylinRedisConst.GROUP_BUY_REMIND_1H.concat(group.getGroupOrderId()), "1",
                RedisKeyExpireConst.KYLIN_GROUP_BUY_EXPIRE);
    }

    private boolean isSmsEnabled() {
        Object v = redisUtil.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())
                    || m.getOrderTicketsId().startsWith("ROBOT_")) {
                continue;
            }
            Integer st = m.getStatus();
            boolean ok = joinedOnly
                    ? GroupBuyStatusConst.MemberStatus.JOINED.equals(st)
                    : 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 = redisUtil.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 ObjectNode buildSuccessParams(KylinGroupBuyOrderVo group) {
        ObjectNode node = JsonUtils.OM().createObjectNode();
        String name = "";
        String time = "";
        String ticket = "";
        try {
            KylinGroupBuyActivityVo activity = mongoTemplate.findOne(
                    Query.query(Criteria.where("activityId").is(group.getActivityId())),
                    KylinGroupBuyActivityVo.class, KylinGroupBuyActivityVo.class.getSimpleName());
            if (activity != null && StringUtils.isNotBlank(activity.getPerformanceId())) {
                KylinPerformanceVo p = dataUtils.getPerformanceVo(activity.getPerformanceId());
                if (p != null) {
                    name = p.getTitle() == null ? "" : p.getTitle();
                    if (p.getTicketTimeList() != null && group.getTimeId() != null) {
                        for (KylinTicketTimesVo t : p.getTicketTimeList()) {
                            if (!group.getTimeId().equals(t.getTimeId())) {
                                continue;
                            }
                            time = t.getTitle() == null ? "" : t.getTitle();
                            if (t.getTicketList() != null && group.getTicketId() != null) {
                                for (KylinTicketVo tk : t.getTicketList()) {
                                    if (group.getTicketId().equals(tk.getTicketsId())) {
                                        ticket = tk.getTitle() == null ? "" : tk.getTitle();
                                        break;
                                    }
                                }
                            }
                            break;
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.warn("拼团成功短信变量组装失败 groupOrderId={}", group.getGroupOrderId(), e);
        }
        node.put("name", name);
        node.put("time", time);
        node.put("ticket", ticket);
        return node;
    }

    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();
                return String.valueOf(hours < 1 ? 1 : 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);
        }
    }
}
