package com.liquidnet.service.platform.utils;

import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.service.base.SqlMapping;
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 lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.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.LocalDateTime;
import java.util.LinkedList;
import java.util.function.Consumer;

/**
 * platform 侧拼团：锁、退款回写、Mongo/Redis/MySQL 快照同步。
 */
@Component
@Slf4j
public class GroupBuyPlatformHelper {

    private static final int LOCK_SECONDS = 15;

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

    public boolean tryLock(String groupOrderId) {
        if (StringUtils.isBlank(groupOrderId)) {
            return false;
        }
        return redisUtil.lock(KylinRedisConst.GROUP_BUY_ORDER_LOCK.concat(groupOrderId), 1, LOCK_SECONDS);
    }

    public void unlock(String groupOrderId) {
        if (StringUtils.isBlank(groupOrderId)) {
            return;
        }
        redisUtil.uLock(KylinRedisConst.GROUP_BUY_ORDER_LOCK.concat(groupOrderId));
    }

    public void withGroupLock(String groupOrderId, Runnable action) {
        if (!tryLock(groupOrderId)) {
            throw new IllegalStateException("拼团处理中，请稍后重试");
        }
        try {
            action.run();
        } finally {
            unlock(groupOrderId);
        }
    }

    public boolean withGroupLockQuiet(String groupOrderId, Consumer<KylinGroupBuyOrderVo> action) {
        if (!tryLock(groupOrderId)) {
            log.warn("拼团锁获取失败 groupOrderId={}", groupOrderId);
            return false;
        }
        try {
            KylinGroupBuyOrderVo group = loadFromMongo(groupOrderId);
            if (group == null) {
                return false;
            }
            action.accept(group);
            return true;
        } finally {
            unlock(groupOrderId);
        }
    }

    /**
     * 票单退款成功：JOINED → REFUNDED；PENDING 团则 count--。幂等。
     */
    public void onTicketRefunded(String orderTicketsId) {
        if (StringUtils.isBlank(orderTicketsId)) {
            return;
        }
        KylinGroupBuyOrderVo group = mongoTemplate.findOne(
                Query.query(Criteria.where("members.orderTicketsId").is(orderTicketsId)),
                KylinGroupBuyOrderVo.class, KylinGroupBuyOrderVo.class.getSimpleName());
        if (group == null || CollectionUtils.isEmpty(group.getMembers())) {
            return;
        }
        String groupOrderId = group.getGroupOrderId();
        if (!tryLock(groupOrderId)) {
            log.warn("退款回写拼团获取锁失败 orderTicketsId={}, groupOrderId={}", orderTicketsId, groupOrderId);
            return;
        }
        try {
            group = loadFromMongo(groupOrderId);
            if (group == null || CollectionUtils.isEmpty(group.getMembers())) {
                return;
            }
            KylinGroupBuyMemberVo member = null;
            for (KylinGroupBuyMemberVo m : group.getMembers()) {
                if (orderTicketsId.equals(m.getOrderTicketsId())) {
                    member = m;
                    break;
                }
            }
            if (member == null) {
                return;
            }
            if (GroupBuyStatusConst.MemberStatus.REFUNDED.equals(member.getStatus())
                    || GroupBuyStatusConst.MemberStatus.CANCELLED.equals(member.getStatus())) {
                return;
            }
            // 仅已支付成员回写；未支付占坑由关单/散团处理
            if (!GroupBuyStatusConst.MemberStatus.JOINED.equals(member.getStatus())) {
                return;
            }
            member.setStatus(GroupBuyStatusConst.MemberStatus.REFUNDED);
            if (GroupBuyStatusConst.OrderStatus.PENDING.equals(group.getStatus())) {
                int count = group.getCurrentCount() == null ? 0 : group.getCurrentCount();
                group.setCurrentCount(Math.max(0, count - 1));
            }
            group.setUpdatedAt(now());
            saveGroup(group);
            log.info("拼团退款回写完成 orderTicketsId={}, groupOrderId={}, groupStatus={}, currentCount={}",
                    orderTicketsId, groupOrderId, group.getStatus(), group.getCurrentCount());
        } catch (Exception e) {
            log.error("拼团退款回写失败 orderTicketsId={}", orderTicketsId, e);
        } finally {
            unlock(groupOrderId);
        }
    }

    public KylinGroupBuyOrderVo loadFromMongo(String groupOrderId) {
        if (StringUtils.isBlank(groupOrderId)) {
            return null;
        }
        return mongoTemplate.findOne(
                Query.query(Criteria.where("groupOrderId").is(groupOrderId)),
                KylinGroupBuyOrderVo.class, KylinGroupBuyOrderVo.class.getSimpleName());
    }

    public void saveGroup(KylinGroupBuyOrderVo group) {
        if (group == null) {
            return;
        }
        clearCache(group);
        mongoTemplate.remove(Query.query(Criteria.where("groupOrderId").is(group.getGroupOrderId())),
                KylinGroupBuyOrderVo.class, KylinGroupBuyOrderVo.class.getSimpleName());
        mongoTemplate.insert(group, KylinGroupBuyOrderVo.class.getSimpleName());
        cache(group);
        sendReplaceSql(group);
    }

    public void cache(KylinGroupBuyOrderVo group) {
        if (group == null) {
            return;
        }
        redisUtil.set(KylinRedisConst.GROUP_BUY_ORDER.concat(group.getGroupOrderId()), group,
                RedisKeyExpireConst.KYLIN_GROUP_BUY_EXPIRE);
        if (StringUtils.isNotBlank(group.getShareToken())) {
            redisUtil.set(KylinRedisConst.GROUP_BUY_ORDER_SHARE.concat(group.getShareToken()), group,
                    RedisKeyExpireConst.KYLIN_GROUP_BUY_EXPIRE);
        }
    }

    public void clearCache(KylinGroupBuyOrderVo group) {
        if (group == null) {
            return;
        }
        redisUtil.del(KylinRedisConst.GROUP_BUY_ORDER.concat(group.getGroupOrderId()));
        if (StringUtils.isNotBlank(group.getShareToken())) {
            redisUtil.del(KylinRedisConst.GROUP_BUY_ORDER_SHARE.concat(group.getShareToken()));
        }
    }

    public void sendReplaceSql(KylinGroupBuyOrderVo group) {
        LinkedList<String> sqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> od = CollectionUtil.linkedListObjectArr();
        LinkedList<Object[]> oi = CollectionUtil.linkedListObjectArr();
        LinkedList<Object[]> md = CollectionUtil.linkedListObjectArr();
        LinkedList<Object[]> mi = CollectionUtil.linkedListObjectArr();
        sqls.add(SqlMapping.get("kylin_group_buy_order.delete"));
        od.add(new Object[]{group.getGroupOrderId()});
        sqls.add(SqlMapping.get("kylin_group_buy_order.insert"));
        oi.add(orderInsertObject(group));
        sqls.add(SqlMapping.get("kylin_group_buy_member.delete_by_group"));
        md.add(new Object[]{group.getGroupOrderId()});
        if (!CollectionUtils.isEmpty(group.getMembers())) {
            sqls.add(SqlMapping.get("kylin_group_buy_member.insert"));
            for (KylinGroupBuyMemberVo m : group.getMembers()) {
                mi.add(memberInsertObject(m));
            }
            queueUtils.sendMsgByRedis(MQConst.KylinQueue.SQL_ORDER_OVERTIME_REFUND.getKey(),
                    SqlMapping.gets(sqls, od, oi, md, mi));
        } else {
            queueUtils.sendMsgByRedis(MQConst.KylinQueue.SQL_ORDER_OVERTIME_REFUND.getKey(),
                    SqlMapping.gets(sqls, od, oi, md));
        }
    }

    private Object[] orderInsertObject(KylinGroupBuyOrderVo vo) {
        return new Object[]{
                vo.getGroupOrderId(), vo.getActivityId(), vo.getLeaderUserId(), vo.getOrderTicketsId(),
                vo.getTargetCount(), vo.getCurrentCount(), vo.getStatus(), vo.getIsPublic(), vo.getShareToken(),
                vo.getExpiresAt() == null ? null : DateUtil.Formatter.yyyyMMddHHmmss.parse(vo.getExpiresAt()),
                DateUtil.Formatter.yyyyMMddHHmmss.parse(vo.getCreatedAt()),
                DateUtil.Formatter.yyyyMMddHHmmss.parse(vo.getUpdatedAt())
        };
    }

    private Object[] memberInsertObject(KylinGroupBuyMemberVo vo) {
        return new Object[]{
                vo.getMemberId(), vo.getGroupOrderId(), vo.getUserId(), vo.getOrderTicketsId(),
                vo.getTicketCount(), vo.getStatus(),
                DateUtil.Formatter.yyyyMMddHHmmss.parse(vo.getJoinTime()),
                DateUtil.Formatter.yyyyMMddHHmmss.parse(vo.getCreatedAt())
        };
    }

    private String now() {
        return DateUtil.Formatter.yyyyMMddHHmmss.format(LocalDateTime.now());
    }
}
