package cn.com.poc.equity.aggregate.impl;

import cn.com.poc.agent_application.entity.BizAgentApplicationInfoEntity;
import cn.com.poc.agent_application.service.BizAgentApplicationInfoService;
import cn.com.poc.common.constant.BizSnKeyEnum;
import cn.com.poc.common.constant.CommonConstant;
import cn.com.poc.common.utils.DateUtils;
import cn.com.poc.common.utils.GeneratingSnUtils;
import cn.com.poc.common.utils.JsonUtils;
import cn.com.poc.common.utils.StringUtils;
import cn.com.poc.equity.aggregate.MemberEquityService;
import cn.com.poc.equity.constants.EquityEnum;
import cn.com.poc.equity.constants.ModifyEventEnum;
import cn.com.poc.equity.domain.MemberEquityInfo;
import cn.com.poc.equity.domain.modifyEquityInfo.InitModifyEventInfo;
import cn.com.poc.equity.domain.modifyEquityInfo.ModifyEventInfo;
import cn.com.poc.equity.domain.modifyEquityInfo.PayModifyEventInfo;
import cn.com.poc.equity.domain.modifyEquityInfo.RollbackModifyEventInfo;
import cn.com.poc.equity.entity.*;
import cn.com.poc.equity.query.QueryEquityTransactionHistoryQueryCondition;
import cn.com.poc.equity.query.QueryEquityTransactionHistoryQueryItem;
import cn.com.poc.equity.query.QueryPointTransactionHistoryQueryCondition;
import cn.com.poc.equity.query.QueryPointTransactionHistoryQueryItem;
import cn.com.poc.equity.service.*;
import cn.com.poc.knowledge.entity.BizKnowledgeInfoEntity;
import cn.com.poc.knowledge.service.BizKnowledgeInfoService;
import cn.com.yict.framemax.core.i18n.I18nMessageException;
import com.alibaba.fastjson.TypeReference;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author alex.yao
 * @date 2024/12/6
 */
@Component
public class MemberEquityServiceImpl implements MemberEquityService {

    final private Logger logger = LoggerFactory.getLogger(MemberEquityService.class);

    @Resource
    private BizMemberPointsService bizMemberPointsService;

    @Resource
    private BizMemberPointTransactionHistoryService bizMemberPointTransactionHistoryService;

    @Resource
    private BizMemberEquityService bizMemberEquityService;

    @Resource
    private BizMemberEquityTransactionHistoryService bizMemberEquityTransactionHistoryService;

    @Resource
    private BizPaymentPackageConfigurationService bizPaymentPackageConfigurationService;


    @Resource
    private BizAgentApplicationInfoService bizAgentApplicationInfoService;

    @Resource
    private BizKnowledgeInfoService bizKnowledgeInfoService;

    @Override
    public void initMemberEquity(Long memberId) throws Exception {
        logger.info("-----初始化用户权益开始 memberId:{}-----", memberId);
        BizMemberEquityEntity memberEquityEntity = bizMemberEquityService.getByMemberId(memberId);
        if (memberEquityEntity == null) {
            BizPaymentPackageConfigurationEntity bizPaymentPackageConfigurationEntity = new BizPaymentPackageConfigurationEntity();
            bizPaymentPackageConfigurationEntity.setType(EquityEnum.TYPE.normal.name());
            bizPaymentPackageConfigurationEntity.setDomain(EquityEnum.DOMAIN.equity.name());
            bizPaymentPackageConfigurationEntity.setIsDeleted(CommonConstant.IsDeleted.N);
            List<BizPaymentPackageConfigurationEntity> paymentPackageConfigurationEntities = bizPaymentPackageConfigurationService.findByExample(bizPaymentPackageConfigurationEntity, null);
            if (CollectionUtils.isEmpty(paymentPackageConfigurationEntities)) {
                logger.error("-----基础权益包配置异常, entity:{}-----", bizPaymentPackageConfigurationEntity);
                throw new I18nMessageException("equity/equity.is.not.exist");
            }
            BizPaymentPackageConfigurationEntity packageConfigurationEntity = paymentPackageConfigurationEntities.get(0);
            // 更新权益等级
            InitModifyEventInfo initModifyEventInfo = new InitModifyEventInfo();
            this.changeEquityLevel(memberId, ModifyEventEnum.gift, EquityEnum.TYPE.normal, EquityEnum.VALIDITY_UNIT.indefinite, initModifyEventInfo);
            // 发放积分
            this.addPoint(memberId, packageConfigurationEntity.getResourceCount().longValue() * 100, EquityEnum.VALIDITY_UNIT.indefinite, ModifyEventEnum.gift, initModifyEventInfo);
        } else {
            logger.warn("-----用户已初始化过权益, memberId:{}-----", memberId);
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public String addPoint(Long memberId, Long pointNum, EquityEnum.VALIDITY_UNIT validityUnit, ModifyEventEnum modifyEventEnum, ModifyEventInfo modifyEventInfo) throws Exception {
        Date expireDate = getExpiredDateTime(validityUnit.name());
        // 需要检查支付订单是否已发放
        checkIsIssue(memberId, modifyEventEnum, modifyEventInfo);

        // 1. 新增数据 biz_member_points
        BizMemberPointsEntity bizMemberPointsEntity = new BizMemberPointsEntity();
        bizMemberPointsEntity.setMemberId(memberId);
        bizMemberPointsEntity.setTotalPoints(pointNum);
        bizMemberPointsEntity.setUsagePoints(pointNum);
        bizMemberPointsEntity.setExpiredTime(expireDate);
        BizMemberPointsEntity pointsEntity = bizMemberPointsService.save(bizMemberPointsEntity);
        // 2. 新增流水数据 biz_member_point_transaction_history
        String transactionSn = GeneratingSnUtils.generationSn(BizSnKeyEnum.pointSn);
        BizMemberPointTransactionHistoryEntity bizMemberPointTransactionHistoryEntity = new BizMemberPointTransactionHistoryEntity();
        bizMemberPointTransactionHistoryEntity.setTransactionSn(transactionSn);
        bizMemberPointTransactionHistoryEntity.setMemberId(memberId);
        bizMemberPointTransactionHistoryEntity.setPointChangeNum(pointNum);
        bizMemberPointTransactionHistoryEntity.setModifyEvent(modifyEventEnum.name());
        bizMemberPointTransactionHistoryEntity.setModifyEventInfo(JsonUtils.serialize(modifyEventInfo));
        bizMemberPointTransactionHistoryEntity.setModifyTargetId(pointsEntity.getId());
        bizMemberPointTransactionHistoryService.save(bizMemberPointTransactionHistoryEntity);
        return transactionSn;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public String reducePoint(Long memberId, Long pointNum, ModifyEventEnum modifyEventEnum, ModifyEventInfo modifyEventInfo) throws Exception {
        if (pointNum <= 0) {
            return "";
        }
        BizMemberPointsEntity bizMemberPointsEntity = new BizMemberPointsEntity();
        bizMemberPointsEntity.setMemberId(memberId);
        bizMemberPointsEntity.setIsDeleted(CommonConstant.IsDeleted.N);
        List<BizMemberPointsEntity> pointsEntities = bizMemberPointsService.findByExample(bizMemberPointsEntity, null);
        if (CollectionUtils.isEmpty(pointsEntities)) {
            throw new I18nMessageException("equity/not.enough.points");
        }
        String reduceSn = GeneratingSnUtils.generationSn(BizSnKeyEnum.pointSn);
        String lockKey = Long.toString(memberId).intern();

        // 针对用户上锁
        synchronized (lockKey) {
            pointsEntities = bizMemberPointsService.findByExample(bizMemberPointsEntity, null);
            //获取用户记录
            List<BizMemberPointsEntity> rechargeEntities = pointsEntities
                    .stream()
                    .filter(entity -> entity.getUsagePoints() > 0 && entity.getExpiredTime().after(new Date()))
                    .sorted(Comparator.comparing(BizMemberPointsEntity::getExpiredTime))
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(rechargeEntities) || rechargeEntities.stream().mapToLong(BizMemberPointsEntity::getUsagePoints).sum() < pointNum) {
                throw new I18nMessageException("equity/not.enough.points");
            }

            for (BizMemberPointsEntity rechargeEntity : rechargeEntities) {
                if (pointNum == 0) {
                    break;
                }
                long freq = 0;
                if (rechargeEntity.getUsagePoints() < pointNum) {
                    pointNum = pointNum - rechargeEntity.getUsagePoints();
                    freq = rechargeEntity.getUsagePoints();
                } else if (rechargeEntity.getUsagePoints() >= pointNum) {
                    freq = pointNum;
                    pointNum = 0L;
                }
                // 扣减积分
                rechargeEntity.decr(freq);
                bizMemberPointsService.update(rechargeEntity);
                //新增流水数据 biz_member_point_transaction_history
                BizMemberPointTransactionHistoryEntity bizMemberPointTransactionHistoryEntity = new BizMemberPointTransactionHistoryEntity();
                bizMemberPointTransactionHistoryEntity.setTransactionSn(reduceSn);
                bizMemberPointTransactionHistoryEntity.setMemberId(memberId);
                bizMemberPointTransactionHistoryEntity.setPointChangeNum(freq);
                bizMemberPointTransactionHistoryEntity.setModifyEvent(modifyEventEnum.name());
                bizMemberPointTransactionHistoryEntity.setModifyEventInfo(JsonUtils.serialize(modifyEventInfo));
                bizMemberPointTransactionHistoryEntity.setModifyTargetId(rechargeEntity.getId());
                bizMemberPointTransactionHistoryService.save(bizMemberPointTransactionHistoryEntity);
            }
        }
        return reduceSn;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rollbackPoint(String transactionSn) throws Exception {
        if (StringUtils.isBlank(transactionSn)) {
            logger.error("-----交易号为空,无法回滚-----");
            return;
        }
        // 获取流水信息
        BizMemberPointTransactionHistoryEntity bizMemberPointTransactionHistoryEntity = new BizMemberPointTransactionHistoryEntity();
        bizMemberPointTransactionHistoryEntity.setTransactionSn(transactionSn);
        bizMemberPointTransactionHistoryEntity.setIsDeleted(CommonConstant.IsDeleted.N);
        List<BizMemberPointTransactionHistoryEntity> pointTransactionHistoryEntities = bizMemberPointTransactionHistoryService.findByExample(bizMemberPointTransactionHistoryEntity, null);
        if (CollectionUtils.isEmpty(pointTransactionHistoryEntities)) {
            throw new I18nMessageException("equity/transaction.history.not.exist");
        }
        // 1. 若是充值，则删除积分数据
        // 2. 若是消费，则恢复积分数据
        String lock = transactionSn.intern();
        synchronized (lock) {
            for (BizMemberPointTransactionHistoryEntity pointTransactionHistoryEntity : pointTransactionHistoryEntities) {
                String modifyEvent = pointTransactionHistoryEntity.getModifyEvent();
                if (ModifyEventEnum.top_up.name().equals(modifyEvent)) {
                    // 删除积分数据
                    bizMemberPointsService.deletedById(pointTransactionHistoryEntity.getModifyTargetId());
                } else if (ModifyEventEnum.use.name().equals(modifyEvent)) {
                    // 恢复积分数据
                    Long pointChangeNum = pointTransactionHistoryEntity.getPointChangeNum();
                    BizMemberPointsEntity bizMemberPointsEntity = bizMemberPointsService.get(pointTransactionHistoryEntity.getModifyTargetId());
                    bizMemberPointsEntity.increase(pointChangeNum);
                    bizMemberPointsService.update(bizMemberPointsEntity);
                } else {
                    throw new I18nMessageException("equity/event.not.support");
                }
                // 保存rollback流水
                RollbackModifyEventInfo modifyEventInfo = new RollbackModifyEventInfo();
                modifyEventInfo.setTransactionSn(transactionSn);
                BizMemberPointTransactionHistoryEntity rollbackTransactionHistoryEntity = new BizMemberPointTransactionHistoryEntity();
                rollbackTransactionHistoryEntity.setTransactionSn(GeneratingSnUtils.generationSn(BizSnKeyEnum.pointSn));
                rollbackTransactionHistoryEntity.setModifyEvent(ModifyEventEnum.rollback.name());
                rollbackTransactionHistoryEntity.setModifyEventInfo(JsonUtils.serialize(modifyEventInfo));
                rollbackTransactionHistoryEntity.setModifyTargetId(pointTransactionHistoryEntity.getModifyTargetId());
                rollbackTransactionHistoryEntity.setPointChangeNum(pointTransactionHistoryEntity.getPointChangeNum());
                bizMemberPointTransactionHistoryService.save(rollbackTransactionHistoryEntity);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String changeEquityLevel(Long memberId, ModifyEventEnum modifyEventEnum, EquityEnum.TYPE type, EquityEnum.VALIDITY_UNIT validityUnit, ModifyEventInfo modifyEventInfo) throws Exception {
        BizMemberEquityEntity memberEquityEntity = bizMemberEquityService.getByMemberId(memberId);
        // 检查是否已发放
        checkEquityIsIssue(memberId, modifyEventEnum, modifyEventInfo);

        // 获取更新权益等级信息
        BizPaymentPackageConfigurationEntity bizPaymentPackageConfigurationEntity = new BizPaymentPackageConfigurationEntity();
        bizPaymentPackageConfigurationEntity.setType(type.name());
        bizPaymentPackageConfigurationEntity.setDomain(EquityEnum.DOMAIN.equity.name());
        bizPaymentPackageConfigurationEntity.setExpiredType(validityUnit.name());
        bizPaymentPackageConfigurationEntity.setIsDeleted(CommonConstant.IsDeleted.N);
        List<BizPaymentPackageConfigurationEntity> bizPaymentPackageConfigurationEntities = bizPaymentPackageConfigurationService.findByExample(bizPaymentPackageConfigurationEntity, null);
        if (CollectionUtils.isEmpty(bizPaymentPackageConfigurationEntities)) {
            throw new I18nMessageException("equity/equity.level.not.exist");
        }
        long targetEquityId = updateEquity(memberEquityEntity, memberId, type, validityUnit, bizPaymentPackageConfigurationEntities.get(0));
        // 保存流水
        String transactionSn = GeneratingSnUtils.generationSn(BizSnKeyEnum.equitySn);
        BizMemberEquityTransactionHistoryEntity bizMemberEquityTransactionHistoryEntity = new BizMemberEquityTransactionHistoryEntity();
        bizMemberEquityTransactionHistoryEntity.setTransactionSn(transactionSn);
        bizMemberEquityTransactionHistoryEntity.setMemberId(memberId);
        bizMemberEquityTransactionHistoryEntity.setOriginalEquityLevel(memberEquityEntity != null && StringUtils.isNotBlank(memberEquityEntity.getEquityLevel()) ? memberEquityEntity.getEquityLevel() : StringUtils.EMPTY);
        bizMemberEquityTransactionHistoryEntity.setModifyEquityLevel(type.name());
        bizMemberEquityTransactionHistoryEntity.setModifyEvent(modifyEventEnum.name());
        bizMemberEquityTransactionHistoryEntity.setModifyEventInfo(JsonUtils.serialize(modifyEventInfo));
        bizMemberEquityTransactionHistoryEntity.setTargetEquityId(targetEquityId);
        bizMemberEquityTransactionHistoryService.save(bizMemberEquityTransactionHistoryEntity);
        return transactionSn;
    }

    @Override
    public Long getMemberPoints(Long memberId) {
        return bizMemberPointsService.getMemberPoints(memberId);
    }

    @Override
    public MemberEquityInfo getEquityInfo(Long memberId) throws Exception {
        BizMemberEquityEntity bizMemberEquityEntity = bizMemberEquityService.getByMemberId(memberId);
        if (bizMemberEquityEntity == null) {
            this.initMemberEquity(memberId);
            bizMemberEquityEntity = bizMemberEquityService.getByMemberId(memberId);
        }
        //获取已创建应用
        BizAgentApplicationInfoEntity bizAgentApplicationInfoEntity = new BizAgentApplicationInfoEntity();
        bizAgentApplicationInfoEntity.setMemberId(memberId.intValue());
        bizAgentApplicationInfoEntity.setIsDeleted(CommonConstant.IsDeleted.N);
        List<BizAgentApplicationInfoEntity> applicationInfoEntities = bizAgentApplicationInfoService.findByExample(bizAgentApplicationInfoEntity, null);
        //获取知识库文件数量
        BizKnowledgeInfoEntity bizKnowledgeInfoEntity = new BizKnowledgeInfoEntity();
        bizKnowledgeInfoEntity.setMemberId(memberId.toString());
        bizKnowledgeInfoEntity.setIsDeleted(CommonConstant.IsDeleted.N);
        List<BizKnowledgeInfoEntity> knowledgeInfoEntities = bizKnowledgeInfoService.findByExample(bizKnowledgeInfoEntity, null);
        Integer kdNum = 0;
        if (CollectionUtils.isNotEmpty(knowledgeInfoEntities)) {
            for (BizKnowledgeInfoEntity entity : knowledgeInfoEntities) {
                if (org.apache.commons.lang3.StringUtils.isNotBlank(entity.getKdIds())) {
                    List<Integer> kdIds = JsonUtils.deSerialize(entity.getKdIds(), new TypeReference<List<Integer>>() {
                    }.getType());
                    kdNum += kdIds.size();
                }
            }
        }

        String equityConfig = bizMemberEquityEntity.getEquityConfig();
        MemberEquityInfo memberEquityInfo = JsonUtils.deSerialize(equityConfig, MemberEquityInfo.class);
        if (memberEquityInfo == null) {
            memberEquityInfo = new MemberEquityInfo();
        }
        EquityEnum.TYPE level = EquityEnum.TYPE.valueOf(bizMemberEquityEntity.getEquityLevel());
        memberEquityInfo.setEquityLevel(level);
        memberEquityInfo.setExpiredDate(bizMemberEquityEntity.getExpiredDate());
        // 如果是永久权益，则不设置过期时间
        if (level.equals(EquityEnum.TYPE.normal)) {
            memberEquityInfo.setExpiredDate(null);
        }
        memberEquityInfo.setUsedAgentNum(applicationInfoEntities.size());
        memberEquityInfo.setUsedKnowledgeNum(kdNum);

        return memberEquityInfo;
    }

    /**
     * 检查权益包是否已发放
     *
     * @param memberId
     * @param modifyEventEnum
     * @param modifyEventInfo
     * @throws Exception
     */
    private void checkEquityIsIssue(Long memberId, ModifyEventEnum modifyEventEnum, ModifyEventInfo modifyEventInfo) throws Exception {
        if (ModifyEventEnum.top_up.equals(modifyEventEnum)) {
            PayModifyEventInfo payModifyEventInfo = (PayModifyEventInfo) modifyEventInfo;
            QueryEquityTransactionHistoryQueryCondition condition = new QueryEquityTransactionHistoryQueryCondition();
            condition.setPayOrderSn(payModifyEventInfo.getPayOrderSn());
            condition.setMemberId(memberId);
            condition.setModifyEvent(ModifyEventEnum.top_up.name());
            List<QueryEquityTransactionHistoryQueryItem> items = bizMemberEquityTransactionHistoryService.queryEquityTransactionHistory(condition, null);
            if (CollectionUtils.isNotEmpty(items)) {
                logger.error("该订单已发放权益,订单流水号:{}, 已发放权益流水号:{}", payModifyEventInfo.getPayOrderSn(), items.get(0).getTransactionSn());
                throw new I18nMessageException("equity/payment.order.is.issued");
            }
        }
    }

    /**
     * 检查支付订单是否已发放
     *
     * @param memberId
     * @param modifyEventEnum
     * @param modifyEventInfo
     * @throws Exception
     */
    private void checkIsIssue(Long memberId, ModifyEventEnum modifyEventEnum, ModifyEventInfo modifyEventInfo) throws Exception {
        if (modifyEventEnum.equals(ModifyEventEnum.top_up)) {
            PayModifyEventInfo payModifyEventInfo = (PayModifyEventInfo) modifyEventInfo;
            QueryPointTransactionHistoryQueryCondition condition = new QueryPointTransactionHistoryQueryCondition();
            condition.setPayOrderSn(payModifyEventInfo.getPayOrderSn());
            condition.setMemberId(memberId);
            condition.setModifyEvent(ModifyEventEnum.top_up.name());
            List<QueryPointTransactionHistoryQueryItem> items = bizMemberPointTransactionHistoryService.queryPointTransactionHistory(condition, null);
            if (CollectionUtils.isNotEmpty(items)) {
                logger.error("-----该支付订单已发放积分, payOrderSn:{} , transaction_id:{}-----", payModifyEventInfo.getPayOrderSn(), items.get(0).getTransactionSn());
                throw new I18nMessageException("equity/payment.order.is.issued");
            }
        }
    }

    /**
     * 更新权益
     *
     * @param memberEquityEntity
     * @param memberId
     * @param type
     * @param validityUnit
     * @param paymentPackageConfigurationEntity
     * @return
     * @throws Exception
     */
    private Long updateEquity(BizMemberEquityEntity memberEquityEntity, Long memberId, EquityEnum.TYPE type, EquityEnum.VALIDITY_UNIT validityUnit, BizPaymentPackageConfigurationEntity paymentPackageConfigurationEntity) throws Exception {
        Date expireDate = getExpiredDateTime(validityUnit.name());
        Long targetEquityId = null;
        if (memberEquityEntity == null) {
            memberEquityEntity = new BizMemberEquityEntity();
            memberEquityEntity.setMemberId(memberId);
            memberEquityEntity.setEquityLevel(type.name());
            memberEquityEntity.setExpiredDate(expireDate);
            memberEquityEntity.setEquityConfig(paymentPackageConfigurationEntity.getExtraData());
            BizMemberEquityEntity saveEquityEntity = bizMemberEquityService.save(memberEquityEntity);
            targetEquityId = saveEquityEntity.getId();
        } else {
            // 判断是否同等级，若是同则过期时间
            if (memberEquityEntity.getEquityLevel().equals(type.name())) {
                expireDate = getExpiredDateTime(validityUnit.name(), memberEquityEntity.getExpiredDate());
            }
            memberEquityEntity.setEquityLevel(type.name());
            memberEquityEntity.setExpiredDate(expireDate);
            memberEquityEntity.setEquityConfig(paymentPackageConfigurationEntity.getExtraData());
            BizMemberEquityEntity updateEquityEntity = bizMemberEquityService.update(memberEquityEntity);
            targetEquityId = updateEquityEntity.getId();
        }
        return targetEquityId;
    }

    /**
     * 获取过期时间
     *
     * @param expiredType
     * @return
     */
    private Date getExpiredDateTime(String expiredType) {
        return this.getExpiredDateTime(expiredType, new Date());
    }

    private Date getExpiredDateTime(String expiredType, Date baseDate) {
        Date expireDate = null;
        if (EquityEnum.VALIDITY_UNIT.indefinite.name().equals(expiredType)) { // 普通权益为永久有效
            expireDate = DateUtils.addYear(new Date(), 200);
        } else {
            expireDate = EquityEnum.VALIDITY_UNIT.month.name().equals(expiredType) ? DateUtils.addMonth(baseDate, 1) : DateUtils.addYear(baseDate, 1);
        }
        return expireDate;
    }
}
