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

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.liquidnet.commons.lang.util.CurrentUtil;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.service.adam.dto.AdamCaomeiPassportUserBadgeDto;
import com.liquidnet.service.adam.dto.vo.*;
import com.liquidnet.service.adam.entity.AdamCaomeiBadge;
import com.liquidnet.service.adam.entity.AdamCaomeiBadgeApplyRecord;
import com.liquidnet.service.adam.entity.AdamCaomeiPassport;
import com.liquidnet.service.adam.mapper.AdamCaomeiBadgeApplyRecordMapper;
import com.liquidnet.service.adam.mapper.AdamCaomeiPassportMapper;
import com.liquidnet.service.adam.service.AdamRdmService;
import com.liquidnet.service.adam.service.IAdamCaomeiPassportUserService;
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.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AdamCaomeiPassportUserServiceImpl implements IAdamCaomeiPassportUserService {

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

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseDto<List<AdamCaomeiPassportUserClaimedBadgeVo>> bindPassport(String passportNo) {
        String uid = CurrentUtil.getCurrentUid();

        // 1. 先校验护照信息是否满足绑定条件
        AdamCaomeiPassport passport = adamCaomeiPassportMapper.selectOne(
                Wrappers.lambdaQuery(AdamCaomeiPassport.class)
                        .eq(AdamCaomeiPassport::getPassportNo, passportNo.trim()));

        if (passport == null) {
            log.error("[bindPassport] 未查询到护照信息, passportSn: {}", passportNo);
            return ResponseDto.failure(ErrorMapping.get("10600"));
        }

        Integer st = passport.getStatus();
        if (st != null && st == 2) {
            log.error("[bindPassport] 护照已作废, passportSn: {}", passportNo);
            return ResponseDto.failure(ErrorMapping.get("10601"));
        }

        if (st != null && st == 1) {
            if (uid.equals(StringUtils.trimToEmpty(passport.getUserId()))) {
                return ResponseDto.failure(ErrorMapping.get("10606")); // 提示无需重复绑定
            }
            log.error("[bindPassport] 护照已被其他账号绑定, passportSn: {}", passportNo);
            return ResponseDto.failure(ErrorMapping.get("10602"));
        }

        // 2. 校验当前用户是否已经绑定过其他护照
        AdamCaomeiPassport userExistsPassport = findBoundPassportForUser(uid);
        if (userExistsPassport != null && !passportNo.equals(userExistsPassport.getPassportNo())) {
            log.error("[bindPassport] 已绑定护照, uid: {}, existsPassportSn: {}, newPassportSn: {}", uid, userExistsPassport.getPassportNo(), passportNo);
            return ResponseDto.failure(ErrorMapping.get("10603"));
        }

        // 3. 尝试原子更新绑定状态 (仅当 status = 0 时才能更新成功)
        int updatedRows = adamCaomeiPassportMapper.bindPassportAtomic(passportNo.trim(), uid);

        if (updatedRows > 0) {
            // 4. 优先复用已上架徽章缓存，再筛选护照纪念徽章(type=1)，用于批量发放和返回给前端弹窗
            List<AdamCaomeiBadge> publishedBadges = adamRdmService.getPublishedCaomeiBadges();
            if (publishedBadges == null) {
                publishedBadges = new ArrayList<>();
            }
            // 所有护照类型徽章
            List<AdamCaomeiBadge> passportTypeBadges = publishedBadges.stream()
                    .filter(b -> b.getType() != null && b.getType() == 1)
                    .collect(Collectors.toList());

            if (passportTypeBadges.isEmpty()) {
                log.info("[bindPassport] 暂无上架徽章");
                return ResponseDto.success();
            }

            List<String> badgeIds = passportTypeBadges.stream()
                    .map(AdamCaomeiBadge::getBadgeId)
                    .filter(StringUtils::isNotBlank)
                    .collect(Collectors.toList());

            Date grantAt = DateUtil.now();

            // 兼容 claimBadge 的写法：把本次发放的护照徽章同时写入用户徽章缓存列表
            List<AdamCaomeiPassportUserBadgeDto> cacheBadgeVos = adamRdmService.getUserCaomeiBadgesByUid(uid);
            if (cacheBadgeVos == null) {
                cacheBadgeVos = new ArrayList<>();
            } else {
                cacheBadgeVos = new ArrayList<>(cacheBadgeVos);
            }
            Set<String> existedBadgeIds = cacheBadgeVos.stream()
                    .map(AdamCaomeiPassportUserBadgeDto::getBadgeId)
                    .filter(StringUtils::isNotBlank)
                    .collect(Collectors.toSet());
            List<AdamCaomeiPassportUserBadgeDto> appendVos = passportTypeBadges.stream()
                    .filter(b -> b != null && StringUtils.isNotBlank(b.getBadgeId()) && !existedBadgeIds.contains(b.getBadgeId()))
                    .map(b -> {
                        AdamCaomeiPassportUserBadgeDto dto = new AdamCaomeiPassportUserBadgeDto();
                        dto.setBadgeId(b.getBadgeId());
                        dto.setBadgeName(StringUtils.defaultString(b.getName()));
                        dto.setIcon(StringUtils.defaultString(b.getIcon()));
                        dto.setShareText(StringUtils.defaultString(b.getShareText()));
                        dto.setType(b.getType());
                        dto.setClaimedAt(grantAt);
                        dto.setSource(1);
                        dto.setSubTitle(b.getSubTitle());
                        return dto;
                    })
                    .collect(Collectors.toList());
            adamRdmService.addUserCaomeiBadgeDtosByUid(uid, cacheBadgeVos, appendVos);

            LinkedList<Object[]> paramsList = new LinkedList<>();
            for (String badgeId : badgeIds) {
                paramsList.add(new Object[]{uid, badgeId, 1, grantAt});
            }
            queueUtils.sendMsgByRedis(
                    MQConst.AdamQueue.SQL_UCENTER.getKey(),
                    SqlMapping.get("adam_caomei_user_badge.add", paramsList)
            );

            // 5. 返回本次绑定场景下的护照类型徽章列表（用于前端弹窗）
            List<AdamCaomeiPassportUserClaimedBadgeVo> grantedBadges = appendVos.stream().map(b -> {
                AdamCaomeiPassportUserClaimedBadgeVo v = new AdamCaomeiPassportUserClaimedBadgeVo();
                v.setBadgeId(b.getBadgeId());
                v.setName(StringUtils.defaultString(b.getBadgeName()));
                v.setIcon(StringUtils.defaultString(b.getIcon()));
                v.setShareText(StringUtils.defaultString(b.getShareText()));
                v.setType(b.getType());
                v.setClaimedAt(b.getClaimedAt());
                v.setSource(1);
                return v;
            }).collect(Collectors.toList());
            log.info("[bindPassport] 护照绑定成功, uid: {}, passportSn: {}", uid, passportNo);
            return ResponseDto.success(grantedBadges);
        }

        // 6. 如果原子性修改未成功，说明在并发情况下被别人抢先绑定了
        log.error("[bindPassport] 并发绑定失败，护照已被抢占, passportSn: {}", passportNo);
        return ResponseDto.failure(ErrorMapping.get("10602"));
    }

    @Override
    public ResponseDto<AdamCaomeiPassportHomeVo> getPassportHome() {
        String uid = CurrentUtil.getCurrentUid();
        log.info("[getPassportHome] 开始获取护照首页数据, uid: {}", uid);

        // 4. 获取用户当前绑定的护照信息
        AdamCaomeiPassport bound = findBoundPassportForUser(uid);
        if (null == bound) {
            log.error("[getPassportHome] 用户未绑定护照, uid: {}", uid);
            return ResponseDto.failure(ErrorMapping.get("10604")); // 未绑定护照
        }

        // 1. 初始化返回对象
        AdamCaomeiPassportHomeVo home = new AdamCaomeiPassportHomeVo();
        AdamCaomeiPassportUserCardVo card = buildUserCard(uid, bound);
        home.setUserCard(card);

        // 3. 获取用户实名认证信息
        AdamRealInfoVo real = adamRdmService.getRealInfoVoByUidPlain(uid);
        String idCard = null;
        boolean isRealNameVerified = false;
        if (real != null && real.getState() != null && real.getState() == 1 && real.getType() == 1) {
            isRealNameVerified = true;
            idCard = real.getIdCard();
        }
        card.setRealNameVerified(isRealNameVerified);
        log.info("[getPassportHome] 实名认证状态, uid: {}, 是否已实名: {}, 是否有身份证号: {}",
                uid, isRealNameVerified, StringUtils.isNotBlank(idCard));

        // 5. TODO 优化点
        final List<String> paidPerformanceIds = StringUtils.isNotBlank(idCard)
                ? adamRdmService.getPaidPerformanceIdsByIdCard(idCard)
                : new ArrayList<>();
        log.info("[getPassportHome] 用户已支付的演出订单数量, uid: {}, 数量: {}", uid, paidPerformanceIds.size());

        // 先取上架徽章配置（供补签状态按演出聚合时做 badgeId→performanceId 兜底）
        List<AdamCaomeiBadge> published = adamRdmService.getPublishedCaomeiBadges();
        if (published == null) {
            published = new ArrayList<>();
        }
        ApplyBadgeStatus applyBadgeStatus = loadApplyBadgeStatus(uid, published);

        // 6. 查询用户已认领的所有徽章记录 (用于展示徽章墙)
        List<AdamCaomeiPassportUserBadgeDto> rows = adamRdmService.getUserCaomeiBadgesByUid(uid);
        if (rows == null) {
            rows = new ArrayList<>();
        }
        Map<String, String> claimedPerformanceTitleById = buildClaimedPerformanceTitleMap(rows);
        List<AdamCaomeiPassportUserClaimedBadgeVo> claimed = toClaimedBadgeVos(rows, claimedPerformanceTitleById);
        home.setClaimedBadges(claimed);
        log.info("[getPassportHome] 用户已认领的徽章数量, uid: {}, 数量: {}", uid, claimed.size());

        // 转换为 Map 方便后续匹配货架上的徽章是否已认领
        Map<String, AdamCaomeiPassportUserBadgeDto> claimedMap = toClaimedBadgeMap(rows);

        log.info("[getPassportHome] 系统已上架的徽章数量, uid: {}, 数量: {}", uid, published.size());

        // 演出纪念徽章：批量查演出名称，供前端按演出分组展示
        Map<String, String> performanceTitleById = buildPerformanceTitleMap(published);

        // 8. 组装全部上架徽章列表 (扁平结构，前端按 type / 演出名称 筛选分组展示)
        List<AdamCaomeiPassportBadgeShelfItemVo> allBadges = toShelfItems(
                published,
                claimedMap,
                paidPerformanceIds,
                applyBadgeStatus,
                performanceTitleById
        );

        home.setAllBadges(allBadges);
        log.info("[getPassportHome] 获取护照首页数据成功, uid: {}", uid);
        return ResponseDto.success(home);
    }

    @Override
    public ResponseDto<Boolean> checkPassportBound() {
        String uid = CurrentUtil.getCurrentUid();
        AdamCaomeiPassport bound = findBoundPassportForUser(uid);
        return ResponseDto.success(bound != null);
    }

    private AdamCaomeiPassportUserCardVo buildUserCard(String uid, AdamCaomeiPassport bound) {
        AdamCaomeiPassportUserCardVo card = new AdamCaomeiPassportUserCardVo();
        card.setPassportBound(true);
        card.setPassportNo(bound.getPassportNo());
        card.setPassportClaimedAt(bound.getBoundAt());

        AdamUserInfoVo userInfo = adamRdmService.getUserInfoVoByUid(uid);
        if (userInfo != null) {
            card.setAvatar(StringUtils.defaultString(userInfo.getAvatar()));
            card.setNickname(StringUtils.defaultString(userInfo.getNickname()));
        } else {
            card.setAvatar("");
            card.setNickname("");
        }
        return card;
    }

    private ApplyBadgeStatus loadApplyBadgeStatus(String uid, List<AdamCaomeiBadge> published) {
        Map<String, String> badgeIdToPerformanceId = new HashMap<>();
        if (published != null) {
            for (AdamCaomeiBadge b : published) {
                if (b != null && StringUtils.isNotBlank(b.getBadgeId())) {
                    badgeIdToPerformanceId.put(b.getBadgeId(), StringUtils.trimToEmpty(b.getPerformanceId()));
                }
            }
        }
        List<AdamCaomeiBadgeApplyRecord> applyRecords = badgeApplyRecordMapper.selectList(
                Wrappers.lambdaQuery(AdamCaomeiBadgeApplyRecord.class)
                        .eq(AdamCaomeiBadgeApplyRecord::getUserId, uid)
                        .in(AdamCaomeiBadgeApplyRecord::getAuditStatus, 0, 1)
        );
        Set<String> passedApplyBadgeIds = new HashSet<>();
        Set<String> pendingApplyBadgeIds = new HashSet<>();
        Set<String> passedApplyPerformanceIds = new HashSet<>();
        Set<String> pendingApplyPerformanceIds = new HashSet<>();
        for (AdamCaomeiBadgeApplyRecord r : applyRecords) {
            if (r == null || StringUtils.isBlank(r.getBadgeId()) || r.getAuditStatus() == null) {
                continue;
            }
            String perf = StringUtils.trimToEmpty(r.getPerformanceId());
            if (StringUtils.isBlank(perf)) {
                perf = StringUtils.trimToEmpty(badgeIdToPerformanceId.get(r.getBadgeId()));
            }
            if (r.getAuditStatus() == 1) {
                passedApplyBadgeIds.add(r.getBadgeId());
                if (StringUtils.isNotBlank(perf)) {
                    passedApplyPerformanceIds.add(perf);
                }
            } else if (r.getAuditStatus() == 0) {
                pendingApplyBadgeIds.add(r.getBadgeId());
                if (StringUtils.isNotBlank(perf)) {
                    pendingApplyPerformanceIds.add(perf);
                }
            }
        }
        return new ApplyBadgeStatus(
                passedApplyBadgeIds,
                pendingApplyBadgeIds,
                passedApplyPerformanceIds,
                pendingApplyPerformanceIds
        );
    }

    private static List<AdamCaomeiPassportUserClaimedBadgeVo> toClaimedBadgeVos(List<AdamCaomeiPassportUserBadgeDto> rows,
                                                                               Map<String, String> performanceTitleById) {
        return rows.stream().map(r -> {
            AdamCaomeiPassportUserClaimedBadgeVo v = new AdamCaomeiPassportUserClaimedBadgeVo();
            v.setBadgeId(r.getBadgeId());
            v.setName(StringUtils.defaultString(r.getBadgeName()));
            v.setSubTitle(StringUtils.defaultString(r.getSubTitle()));
            v.setIcon(StringUtils.defaultString(r.getIcon()));
            v.setShareText(StringUtils.defaultString(r.getShareText()));
            v.setType(r.getType());
            if (r.getType() != null && r.getType() == 2 && StringUtils.isNotBlank(r.getPerformanceId())) {
                String title = performanceTitleById != null ? performanceTitleById.get(r.getPerformanceId()) : null;
                v.setPerformanceName(StringUtils.isNotBlank(title) ? title : r.getPerformanceId());
            } else {
                v.setPerformanceName("");
            }
            v.setClaimedAt(r.getClaimedAt());
            v.setSource(r.getSource());
            return v;
        }).collect(Collectors.toList());
    }

    private Map<String, String> buildClaimedPerformanceTitleMap(List<AdamCaomeiPassportUserBadgeDto> claimedRows) {
        List<String> perfIds = claimedRows.stream()
                .filter(r -> r != null && r.getType() != null && r.getType() == 2)
                .map(AdamCaomeiPassportUserBadgeDto::getPerformanceId)
                .filter(StringUtils::isNotBlank)
                .distinct()
                .collect(Collectors.toList());
        if (perfIds.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<String, String> map = new HashMap<>(perfIds.size() * 2);
        for (String perfId : perfIds) {
            String title = adamRdmService.getPerformanceTitleById(perfId);
            if (StringUtils.isNotBlank(title)) {
                map.put(perfId, title);
            }
        }
        return map;
    }

    private static Map<String, AdamCaomeiPassportUserBadgeDto> toClaimedBadgeMap(List<AdamCaomeiPassportUserBadgeDto> rows) {
        return rows.stream()
                .filter(r -> StringUtils.isNotBlank(r.getBadgeId()))
                .collect(Collectors.toMap(AdamCaomeiPassportUserBadgeDto::getBadgeId, Function.identity(), (a, b) -> a));
    }

    private Map<String, String> buildPerformanceTitleMap(List<AdamCaomeiBadge> published) {
        List<String> perfIds = published.stream()
                .filter(b -> b != null && b.getType() != null && b.getType() == 2)
                .map(AdamCaomeiBadge::getPerformanceId)
                .filter(StringUtils::isNotBlank)
                .distinct()
                .collect(Collectors.toList());
        if (perfIds.isEmpty()) {
            return Collections.emptyMap();
        }

        Map<String, String> map = new HashMap<>(perfIds.size() * 2);
        for (String perfId : perfIds) {
            String title = adamRdmService.getPerformanceTitleById(perfId);
            if (StringUtils.isNotBlank(title)) {
                map.put(perfId, title);
            }
        }
        return map;
    }

    private static List<AdamCaomeiPassportBadgeShelfItemVo> toShelfItems(List<AdamCaomeiBadge> published,
                                                                          Map<String, AdamCaomeiPassportUserBadgeDto> claimedMap,
                                                                          List<String> paidPerformanceIds,
                                                                          ApplyBadgeStatus applyBadgeStatus,
                                                                          Map<String, String> performanceTitleById) {
        return published.stream()
                .map(b -> toShelfItem(b, claimedMap, paidPerformanceIds, applyBadgeStatus, performanceTitleById))
                .collect(Collectors.toList());
    }

    /**
     * @param b                  徽章信息
     * @param claimedMap         用户已领取的徽章
     * @param paidPerformanceIds 当前账号实名身份证号码购买的演出IDs
     * @param applyBadgeStatus   补签审核状态（按徽章 + 按同场次演出聚合）
     * @param performanceTitleById 演出 ID → 演出名称（仅 type=2 使用）
     * @return
     */
    private static AdamCaomeiPassportBadgeShelfItemVo toShelfItem(AdamCaomeiBadge b,
                                                                  Map<String, AdamCaomeiPassportUserBadgeDto> claimedMap,
                                                                  List<String> paidPerformanceIds,
                                                                  ApplyBadgeStatus applyBadgeStatus,
                                                                  Map<String, String> performanceTitleById) {
        AdamCaomeiPassportBadgeShelfItemVo v = new AdamCaomeiPassportBadgeShelfItemVo();
        v.setBadgeId(b.getBadgeId());
        v.setName(StringUtils.defaultString(b.getName()));
        v.setSubTitle(StringUtils.defaultString(b.getSubTitle()));
        v.setIcon(StringUtils.defaultString(b.getIcon()));
        v.setShareText(StringUtils.defaultString(b.getShareText()));
        v.setType(b.getType());
        String perfId = StringUtils.defaultString(b.getPerformanceId());
        v.setPerformanceId(perfId);
        int type = b.getType() == null ? 0 : b.getType();
        if (type == 2 && StringUtils.isNotBlank(perfId)) {
            String title = performanceTitleById != null ? performanceTitleById.get(perfId) : null;
            v.setPerformanceName(StringUtils.isNotBlank(title) ? title : perfId);
        }
        v.setApplyPending(false);

        // 判断当前徽章是否已认领
        AdamCaomeiPassportUserBadgeDto got = claimedMap.get(b.getBadgeId());
        boolean claimed = got != null;
        v.setClaimed(claimed);
        v.setClaimedAt(got != null ? got.getClaimedAt() : null);

        // 针对未认领的徽章，根据类型判断是否可认领 (claimable)
        if (!claimed) {
            if (type == 1) {
                // 护照纪念徽章：只要绑定了护照，就可认领（通常是绑定时漏发或后来新上架的）
                v.setClaimable(true);
            } else if (type == 2) {
                // 演出纪念徽章：有购票记录，或本场次任一补签审核通过，则本场次关联徽章均可认领
                boolean canClaimByPaid = paidPerformanceIds != null && paidPerformanceIds.contains(b.getPerformanceId());
                boolean canClaimByApplyThisBadge = applyBadgeStatus.getPassedApplyBadgeIds().contains(b.getBadgeId());
                boolean canClaimByApplyThisPerf = StringUtils.isNotBlank(perfId)
                        && applyBadgeStatus.getPassedApplyPerformanceIds().contains(perfId);
                v.setClaimable(canClaimByPaid || canClaimByApplyThisBadge || canClaimByApplyThisPerf);
                boolean pendingThisBadge = applyBadgeStatus.getPendingApplyBadgeIds().contains(b.getBadgeId());
                boolean pendingThisPerf = StringUtils.isNotBlank(perfId)
                        && applyBadgeStatus.getPendingApplyPerformanceIds().contains(perfId);
                v.setApplyPending(pendingThisBadge || pendingThisPerf);
            } else if (type == 3) {
                // 特殊徽章：不可自助领取，需要提示用户
                v.setClaimable(false);
            }
        } else {
            // 已认领的徽章，可认领状态置为 false
            v.setClaimable(false);
        }
        return v;
    }

    private AdamCaomeiPassport findBoundPassportForUser(String uid) {
        if (StringUtils.isBlank(uid)) {
            return null;
        }
        return adamCaomeiPassportMapper.selectOne(
                Wrappers.lambdaQuery(AdamCaomeiPassport.class)
                        .eq(AdamCaomeiPassport::getUserId, uid)
                        .eq(AdamCaomeiPassport::getStatus, 1)
                        .last("limit 1")
        );
    }

    private static final class ApplyBadgeStatus {
        private final Set<String> passedApplyBadgeIds;
        private final Set<String> pendingApplyBadgeIds;
        private final Set<String> passedApplyPerformanceIds;
        private final Set<String> pendingApplyPerformanceIds;

        private ApplyBadgeStatus(Set<String> passedApplyBadgeIds,
                                 Set<String> pendingApplyBadgeIds,
                                 Set<String> passedApplyPerformanceIds,
                                 Set<String> pendingApplyPerformanceIds) {
            this.passedApplyBadgeIds = passedApplyBadgeIds;
            this.pendingApplyBadgeIds = pendingApplyBadgeIds;
            this.passedApplyPerformanceIds = passedApplyPerformanceIds;
            this.pendingApplyPerformanceIds = pendingApplyPerformanceIds;
        }

        Set<String> getPassedApplyBadgeIds() {
            return passedApplyBadgeIds;
        }

        Set<String> getPendingApplyBadgeIds() {
            return pendingApplyBadgeIds;
        }

        Set<String> getPassedApplyPerformanceIds() {
            return passedApplyPerformanceIds;
        }

        Set<String> getPendingApplyPerformanceIds() {
            return pendingApplyPerformanceIds;
        }
    }
}
