package com.liquidnet.service.adam.service.caomei;

import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.service.adam.dto.AdamCaomeiPassportUserBadgeDto;
import com.liquidnet.service.adam.entity.AdamCaomeiBadge;
import com.liquidnet.service.adam.service.AdamRdmService;
import com.liquidnet.service.adam.util.QueueUtils;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 草莓徽章「发放」统一入口：写入用户徽章 Redis（{@code INFO_CAOMEI_BADGE_USER + uid}），
 * 并通过 MQ 异步 INSERT {@code adam_caomei_user_badge}。
 * <p>
 * 调用方：绑护照发 type=1、用户 claim、home 静默发 type=4 签证页。
 * <p>
 * {@code source} 与库表一致：1-绑定护照，2-购票自动，3-补签通过，4-现场管理员（本服务未使用 4）。
 */
@Slf4j
@Service
public class CaomeiBadgeGrantService {

    @Autowired
    private AdamRdmService adamRdmService;
    @Autowired
    private QueueUtils queueUtils;

    /**
     * 将运营配置的徽章实体转为用户已获列表中的缓存 DTO（尚未写 Redis）。
     *
     * @param badge     上架徽章配置（含 badgeId、名称、type、performanceId 等）
     * @param source    获得途径，见类说明
     * @param claimedAt 获得时间，null 时使用当前时间
     */
    public AdamCaomeiPassportUserBadgeDto toUserBadgeDto(AdamCaomeiBadge badge, int source, Date claimedAt) {
        AdamCaomeiPassportUserBadgeDto dto = new AdamCaomeiPassportUserBadgeDto();
        dto.setBadgeId(badge.getBadgeId());
        dto.setBadgeName(StringUtils.defaultString(badge.getName()));
        dto.setSubTitle(StringUtils.defaultString(badge.getSubTitle()));
        dto.setIcon(StringUtils.defaultString(badge.getIcon()));
        dto.setShareText(StringUtils.defaultString(badge.getShareText()));
        dto.setType(badge.getType());
        dto.setPerformanceId(StringUtils.trimToEmpty(badge.getPerformanceId()));
        dto.setClaimedAt(claimedAt != null ? claimedAt : DateUtil.now());
        dto.setSource(source);
        return dto;
    }

    /**
     * 批量发放：对比用户当前已拥有 badgeId，仅对「尚未拥有」的徽章追加缓存并落库。
     * <p>
     * 适用于：绑护照一次性发多个 type=1、home 末尾静默发多个 type=4。
     *
     * @param uid     当前用户 ID
     * @param badges  待发放的配置列表（通常来自 published 缓存筛选）
     * @param source  获得途径
     * @param grantAt 统一获得时间（批量场景 bind/签证 使用同一时间戳），null 则取当前时间
     * @return 本次实际写入 Redis 的 DTO；空列表表示全部已拥有、未发生发放
     */
    public List<AdamCaomeiPassportUserBadgeDto> grantBadgesIfAbsent(String uid,
                                                                    List<AdamCaomeiBadge> badges,
                                                                    int source,
                                                                    Date grantAt) {
        if (StringUtils.isBlank(uid) || CollectionUtils.isEmpty(badges)) {
            return new ArrayList<>();
        }
        Date at = grantAt != null ? grantAt : DateUtil.now();

        List<AdamCaomeiPassportUserBadgeDto> cacheList = copyUserBadgeCache(uid);
        Set<String> existedBadgeIds = cacheList.stream()
                .map(AdamCaomeiPassportUserBadgeDto::getBadgeId)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toSet());

        List<AdamCaomeiPassportUserBadgeDto> appendVos = badges.stream()
                .filter(b -> b != null && StringUtils.isNotBlank(b.getBadgeId()))
                .filter(b -> !existedBadgeIds.contains(b.getBadgeId()))
                .map(b -> toUserBadgeDto(b, source, at))
                .collect(Collectors.toList());
        if (appendVos.isEmpty()) {
            return appendVos;
        }

        adamRdmService.addUserCaomeiBadgeDtosByUid(uid, cacheList, appendVos);
        sendUserBadgeInsertMq(uid, appendVos, source, at);
        return appendVos;
    }

    /**
     * 单条发放：用于 POST claim 循环内逐枚写入。
     * <p>
     * 调用方须已做「未领取」校验；本方法不再查重。
     *
     * @param uid               用户 ID
     * @param badge             单枚上架徽章配置
     * @param source            获得途径（type=2 时由 Eligibility 解析为 2 或 3）
     * @param mutableCacheList  当前请求内持有的用户徽章列表副本，发放后会 append 并写回 Redis
     */
    public void grantOne(String uid,
                         AdamCaomeiBadge badge,
                         int source,
                         List<AdamCaomeiPassportUserBadgeDto> mutableCacheList) {
        if (badge == null || StringUtils.isBlank(badge.getBadgeId())) {
            return;
        }
        Date now = new Date();
        AdamCaomeiPassportUserBadgeDto dto = toUserBadgeDto(badge, source, now);
        adamRdmService.addUserCaomeiBadgeDtoByUid(uid, mutableCacheList, dto);
        queueUtils.sendMsgByRedis(
                MQConst.AdamQueue.SQL_UCENTER.getKey(),
                SqlMapping.get("adam_caomei_user_badge.add", uid, badge.getBadgeId(), source, now)
        );
    }

    /** 读取用户徽章 Redis，返回可修改副本（miss 时可能为空列表） */
    private List<AdamCaomeiPassportUserBadgeDto> copyUserBadgeCache(String uid) {
        List<AdamCaomeiPassportUserBadgeDto> cache = adamRdmService.getUserCaomeiBadgesByUid(uid);
        if (cache == null) {
            return new ArrayList<>();
        }
        return new ArrayList<>(cache);
    }

    /** 批量 MQ：仅对 appendVos 中的 badgeId 发送 INSERT，与 Redis 追加范围一致 */
    private void sendUserBadgeInsertMq(String uid,
                                       List<AdamCaomeiPassportUserBadgeDto> appendVos,
                                       int source,
                                       Date grantAt) {
        LinkedList<Object[]> paramsList = new LinkedList<>();
        for (AdamCaomeiPassportUserBadgeDto dto : appendVos) {
            if (dto == null || StringUtils.isBlank(dto.getBadgeId())) {
                continue;
            }
            paramsList.add(new Object[]{uid, dto.getBadgeId(), source, grantAt});
        }
        if (paramsList.isEmpty()) {
            return;
        }
        queueUtils.sendMsgByRedis(
                MQConst.AdamQueue.SQL_UCENTER.getKey(),
                SqlMapping.get("adam_caomei_user_badge.add", paramsList)
        );
    }
}
