package com.liquidnet.service.adam.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.service.adam.dto.AdamCaomeiPassportUserBadgeDto;
import com.liquidnet.service.adam.dto.param.AdamCaomeiBadgeApplyParam;
import com.liquidnet.service.adam.dto.AdamCaomeiBadgeApplyRecordUserDto;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiBadgeApplyRecordUserVo;
import com.liquidnet.service.adam.dto.vo.AdamRealInfoVo;
import com.liquidnet.service.adam.entity.AdamCaomeiBadgeApplyRecord;
import com.liquidnet.service.adam.entity.AdamCaomeiBadge;
import com.liquidnet.service.adam.entity.AdamCaomeiPassport;
import com.liquidnet.service.adam.mapper.AdamCaomeiBadgeApplyRecordMapper;
import com.liquidnet.service.adam.mapper.AdamCaomeiBadgeMapper;
import com.liquidnet.service.adam.mapper.AdamCaomeiPassportMapper;
import com.liquidnet.service.adam.service.AdamRdmService;
import com.liquidnet.service.adam.service.IAdamCaomeiBadgeUserService;
import com.liquidnet.service.adam.util.QueueUtils;
import com.liquidnet.service.base.ErrorMapping;
import com.liquidnet.service.base.ResponseDto;
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.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AdamCaomeiBadgeUserServiceImpl implements IAdamCaomeiBadgeUserService {

    @Autowired
    private AdamCaomeiBadgeMapper adamCaomeiBadgeMapper;
    @Autowired
    private AdamCaomeiPassportMapper adamCaomeiPassportMapper;
    @Autowired
    private AdamCaomeiBadgeApplyRecordMapper badgeApplyRecordMapper;
    @Autowired
    private AdamRdmService adamRdmService;
    @Autowired
    private QueueUtils queueUtils;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseDto<String> claimBadge(String badgeId, String uid) {
        if (StringUtils.isBlank(uid)) {
            return ResponseDto.failure(ErrorMapping.get("10001"));
        }

        // 1. 查询徽章配置
        AdamCaomeiBadge badge = adamCaomeiBadgeMapper.selectOne(
                Wrappers.lambdaQuery(AdamCaomeiBadge.class)
                        .eq(AdamCaomeiBadge::getBadgeId, badgeId)
                        .eq(AdamCaomeiBadge::getDisplayStatus, 1)
        );
        if (badge == null) {
            log.error("[claimBadge] 徽章不存在或未上架, uid: {}, badgeId: {}", uid, badgeId);
            return ResponseDto.failure(ErrorMapping.get("10607"));
        }

        // 2. 与收货地址 add 一致：先取 Redis（或回源）用户徽章列表，再判重；DB 再兜一层防缓存未命中或其它写入路径
        List<AdamCaomeiPassportUserBadgeDto> badgeVos = adamRdmService.getUserCaomeiBadgesByUid(uid);
        if (badgeVos == null) {
            badgeVos = new ArrayList<>();
        } else {
            badgeVos = new ArrayList<>(badgeVos);
        }
        if (badgeVos.stream().anyMatch(b -> badgeId.equals(b.getBadgeId()))) {
            log.info("[claimBadge] 用户已领取过该徽章(Redis列表), uid: {}, badgeId: {}", uid, badgeId);
            return ResponseDto.failure(ErrorMapping.get("10608"));
        }
        int count = adamCaomeiBadgeMapper.checkUserBadgeExists(uid, badgeId);
        if (count > 0) {
            log.info("[claimBadge] 用户已领取过该徽章, uid: {}, badgeId: {}", uid, badgeId);
            return ResponseDto.failure(ErrorMapping.get("10608"));
        }

        int type = badge.getType() == null ? 0 : badge.getType();

        if (type == 1) {
            AdamCaomeiPassport bound = adamCaomeiPassportMapper.selectOne(
                    Wrappers.lambdaQuery(AdamCaomeiPassport.class)
                            .eq(AdamCaomeiPassport::getUserId, uid)
                            .eq(AdamCaomeiPassport::getStatus, 1)
                            .last("limit 1")
            );
            if (bound == null) {
                log.error("[claimBadge] 认领护照纪念徽章需先绑定护照, uid: {}, badgeId: {}", uid, badgeId);
                return ResponseDto.failure(ErrorMapping.get("10609"));
            }

            // 发放徽章 (source: 1-绑定护照自动发放/护照徽章认领)：先写 Redis，再 MQ 落库
            grantUserBadgeRedisThenMq(uid, badge, 1, badgeVos);
            log.info("[claimBadge] 护照纪念徽章认领成功, uid: {}, badgeId: {}", uid, badgeId);
            return ResponseDto.success(badgeId);

        } else if (type == 2) {
            AdamRealInfoVo real = adamRdmService.getRealInfoVoByUidPlain(uid);
            if (real == null || real.getState() == null || real.getState() != 1 || StringUtils.isBlank(real.getIdCard())) {
                log.error("[claimBadge] 认领演出徽章需先实名, uid: {}, badgeId: {}", uid, badgeId);
                return ResponseDto.failure(ErrorMapping.get("10610"));
            }

            String idCard = real.getIdCard();
            List<String> paidPerformanceIds = adamRdmService.getPaidPerformanceIdsByIdCard(idCard);
            boolean hasPaidRecord = paidPerformanceIds != null && paidPerformanceIds.contains(badge.getPerformanceId());
            Integer passedApplyCount = badgeApplyRecordMapper.selectCount(
                    Wrappers.lambdaQuery(AdamCaomeiBadgeApplyRecord.class)
                            .eq(AdamCaomeiBadgeApplyRecord::getUserId, uid)
                            .eq(AdamCaomeiBadgeApplyRecord::getBadgeId, badgeId)
                            .eq(AdamCaomeiBadgeApplyRecord::getAuditStatus, 1)
            );
            boolean hasPassedApply = passedApplyCount != null && passedApplyCount > 0;

            if (!hasPaidRecord && !hasPassedApply) {
                log.error("[claimBadge] 无购票记录且无通过的补签申请，无法认领, uid: {}, badgeId: {}", uid, badgeId);
                return ResponseDto.failure(ErrorMapping.get("10611"));
            }

            int source = hasPaidRecord ? 2 : 3;
            grantUserBadgeRedisThenMq(uid, badge, source, badgeVos);

            log.info("[claimBadge] 演出纪念徽章认领成功, uid: {}, badgeId: {}, 发放数量: {}", uid, badgeId, 1);
            return ResponseDto.success(badgeId);

        } else if (type == 3) {
            log.error("[claimBadge] 特殊徽章不可自助领取, uid: {}, badgeId: {}", uid, badgeId);
            return ResponseDto.failure(ErrorMapping.get("10612"));
        }

        log.error("[claimBadge] 未知的徽章类型, uid: {}, badgeId: {}, type: {}", uid, badgeId, type);
        return ResponseDto.failure(ErrorMapping.get("10613"));
    }

    /**
     * Redis 追加用户徽章展示 DTO，再发 MQ 异步执行 sqlmap 中的 INSERT。
     */
    private void grantUserBadgeRedisThenMq(String uid, AdamCaomeiBadge badge, int source,
                                           List<AdamCaomeiPassportUserBadgeDto> badgeVos) {
        Date now = new Date();
        AdamCaomeiPassportUserBadgeDto dto = new AdamCaomeiPassportUserBadgeDto();
        dto.setBadgeId(badge.getBadgeId());
        dto.setBadgeName(StringUtils.defaultString(badge.getName()));
        dto.setIcon(StringUtils.defaultString(badge.getIcon()));
        dto.setShareText(StringUtils.defaultString(badge.getShareText()));
        dto.setType(badge.getType());
        dto.setClaimedAt(now);
        dto.setSource(source);

        adamRdmService.addUserCaomeiBadgeDtoByUid(uid, badgeVos, dto);

        long t = System.currentTimeMillis();
        queueUtils.sendMsgByRedis(
                MQConst.AdamQueue.SQL_UCENTER.getKey(),
                SqlMapping.get("adam_caomei_user_badge.add", uid, badge.getBadgeId(), source, now)
        );
        log.debug("[claimBadge] MQ耗时:{}ms, uid: {}, badgeId: {}", System.currentTimeMillis() - t, uid, badge.getBadgeId());
    }

    @Override
    public ResponseDto<List<AdamCaomeiBadgeApplyRecordUserVo>> getApplyRecords(String uid) {
        if (StringUtils.isBlank(uid)) {
            return ResponseDto.failure(ErrorMapping.get("10001"));
        }
        List<AdamCaomeiBadgeApplyRecordUserDto> rows = badgeApplyRecordMapper.selectUserApplyRecordsByUid(uid);
        if (rows == null) {
            rows = Collections.emptyList();
        }
        List<AdamCaomeiBadgeApplyRecordUserVo> list = rows.stream().map(r -> {
            AdamCaomeiBadgeApplyRecordUserVo v = new AdamCaomeiBadgeApplyRecordUserVo();
            v.setApplyRecordId(r.getApplyRecordId());
            v.setBadgeName(StringUtils.defaultString(r.getBadgeName()));
            v.setApplyTime(r.getApplyTime());
            v.setAuditStatus(r.getAuditStatus());
            v.setRejectReason(StringUtils.defaultString(r.getRejectReason()));
            v.setCanReupload(r.getAuditStatus() != null && r.getAuditStatus() == 2);
            v.setProofImageUrl(r.getProofImageUrl());
            return v;
        }).collect(Collectors.toList());
        return ResponseDto.success(list);
    }

    @Override
    public ResponseDto<String> applyBadge(AdamCaomeiBadgeApplyParam param, String uid) {
        if (StringUtils.isBlank(uid)) {
            return ResponseDto.failure(ErrorMapping.get("10001"));
        }
        if (param == null || StringUtils.isBlank(param.getBadgeId()) || StringUtils.isBlank(param.getProofImageUrl())) {
            return ResponseDto.failure("参数错误");
        }
        String badgeId = StringUtils.trimToEmpty(param.getBadgeId());
        String proofImageUrl = StringUtils.trimToEmpty(param.getProofImageUrl());

        AdamCaomeiBadge badge = adamCaomeiBadgeMapper.selectOne(
                Wrappers.lambdaQuery(AdamCaomeiBadge.class)
                        .eq(AdamCaomeiBadge::getBadgeId, badgeId)
                        .last("limit 1")
        );
        if (badge == null) {
            return ResponseDto.failure(ErrorMapping.get("10607"));
        }
        if (badge.getType() == null || badge.getType() != 2) {
            return ResponseDto.failure("仅演出纪念徽章支持补签申请");
        }

        int claimedCount = adamCaomeiBadgeMapper.checkUserBadgeExists(uid, badgeId);
        if (claimedCount > 0) {
            return ResponseDto.failure(ErrorMapping.get("10608"));
        }

        Integer pendingCount = badgeApplyRecordMapper.selectCount(
                Wrappers.lambdaQuery(AdamCaomeiBadgeApplyRecord.class)
                        .eq(AdamCaomeiBadgeApplyRecord::getUserId, uid)
                        .eq(AdamCaomeiBadgeApplyRecord::getBadgeId, badgeId)
                        .eq(AdamCaomeiBadgeApplyRecord::getAuditStatus, 0)
        );
        if (pendingCount != null && pendingCount > 0) {
            return ResponseDto.failure("您已有待审核的补签申请，请勿重复提交");
        }

        final String applyRecordId = IDGenerator.nextSnowId();
        long t = System.currentTimeMillis();
        String performanceId = StringUtils.trimToEmpty(badge.getPerformanceId());
        boolean autoPass = isPerformanceOngoing(performanceId);
        if (autoPass) {
            // 与 admin 审核通过保持一致：仅将申请记录标记为已通过，不自动发放徽章
            queueUtils.sendMsgByRedis(
                    MQConst.AdamQueue.SQL_UCENTER.getKey(),
                    SqlMapping.get("adam_caomei_badge_apply_record.add_passed",
                            applyRecordId,
                            uid,
                            badgeId,
                            performanceId,
                            proofImageUrl)
            );
        } else {
            queueUtils.sendMsgByRedis(
                    MQConst.AdamQueue.SQL_UCENTER.getKey(),
                    SqlMapping.get("adam_caomei_badge_apply_record.add",
                            applyRecordId,
                            uid,
                            badgeId,
                            performanceId,
                            proofImageUrl)
            );
        }
        log.info("[claimBadge] MQ耗时:{}ms, uid: {}, badgeId: {}", System.currentTimeMillis() - t, uid, badge.getBadgeId());

        return ResponseDto.success(applyRecordId);
    }

    private boolean isPerformanceOngoing(String performanceId) {
        if (StringUtils.isBlank(performanceId)) {
            return false;
        }
        AdamRdmService.PerformanceTimeRange r = adamRdmService.getPerformanceTimeRangeById(performanceId);
        if (r == null || r.getStart() == null || r.getEnd() == null) {
            return false;
        }
        java.time.LocalDateTime now = java.time.LocalDateTime.now();
        return (now.isEqual(r.getStart()) || now.isAfter(r.getStart()))
                && (now.isEqual(r.getEnd()) || now.isBefore(r.getEnd()));
    }
}
