package com.czg.service.market.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.czg.account.dto.shopuser.ShopUserMoneyEditDTO;
import com.czg.account.entity.ShopInfo;
import com.czg.account.entity.ShopUser;
import com.czg.account.service.ShopConfigService;
import com.czg.account.service.ShopInfoService;
import com.czg.account.service.ShopUserService;
import com.czg.config.RabbitPublisher;
import com.czg.constant.TableValueConstant;
import com.czg.enums.ShopUserFlowBizEnum;
import com.czg.exception.CzgException;
import com.czg.market.dto.CouponInfoDTO;
import com.czg.market.dto.MkRewardCouponDTO;
import com.czg.market.dto.MkShopRechargeDTO;
import com.czg.market.entity.MkShopRechargeDetail;
import com.czg.market.entity.ShopCoupon;
import com.czg.market.enums.PointsConstant;
import com.czg.market.service.*;
import com.czg.market.vo.*;
import com.czg.print.RechargePrintDTO;
import com.czg.utils.AssertUtil;
import com.czg.utils.FunUtils;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.czg.market.entity.MkShopRecharge;
import com.czg.service.market.mapper.MkShopRechargeMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 充值配置表 服务层实现。
 *
 * @author zs
 * @since 2025-09-16
 */
@Slf4j
@Service
public class MkShopRechargeServiceImpl extends ServiceImpl<MkShopRechargeMapper, MkShopRecharge> implements MkShopRechargeService {
    @Resource
    private MkShopRechargeDetailService shopRechargeDetailService;
    @Resource
    private TbMemberConfigService memberConfigService;
    @Resource
    private ShopCouponService shopCouponService;
    @DubboReference
    private ShopUserService shopUserService;
    @Resource
    private MkPointsUserService pointsUserService;
    @Resource
    private MkShopCouponRecordService shopCouponRecordService;
    @DubboReference
    private ShopInfoService shopInfoService;
    @DubboReference
    private ShopConfigService shopConfigService;
    @Resource
    private RabbitPublisher rabbitPublisher;

    @Override
    public MkShopRechargeVO detailApp(Long shopId) {
        Long mainShopId = shopInfoService.getMainIdByShopId(shopId);
        MkShopRechargeVO rechargeVO = detail(mainShopId);
        if ("part".equals(rechargeVO.getUseType())) {
            return rechargeVO.getShopIdList().contains(shopId) ? rechargeVO : null;
        }
        return rechargeVO;
    }

    @Override
    public MkShopRechargeVO detail(Long shopId) throws CzgException {
        shopId = shopInfoService.getMainIdByShopId(shopId);
        ShopInfo shopInfo = shopInfoService.getById(shopId);
        if (shopInfo.getMainId() != null) {
            shopId = shopInfo.getMainId();
        }
        MkShopRecharge shopRecharge = getOne(new QueryWrapper().eq(MkShopRecharge::getMasterShopId, shopId));
        if (shopRecharge == null) {
            shopRecharge = new MkShopRecharge();
            shopRecharge.setMasterShopId(shopId);
            save(shopRecharge);
            shopRecharge = getOne(new QueryWrapper().eq(MkShopRecharge::getMasterShopId, shopId));
        }

        MkShopRechargeVO shopRechargeVO = BeanUtil.copyProperties(shopRecharge, MkShopRechargeVO.class, "shopIdList");
        if (StrUtil.isNotBlank(shopRecharge.getShopIdList())) {
            shopRechargeVO.setShopIdList(JSONArray.parseArray(shopRecharge.getShopIdList()).toList(Long.class));
        }

        List<MkShopRechargeDetailVO> detailList = shopRechargeDetailService.list(new QueryWrapper().eq(MkShopRechargeDetail::getShopRechargeId, shopRecharge.getId())).stream().map(mkShopRechargeDetail -> {
            MkShopRechargeDetailVO detailVO = BeanUtil.copyProperties(mkShopRechargeDetail, MkShopRechargeDetailVO.class, "couponInfoList");
            if (StrUtil.isNotBlank(mkShopRechargeDetail.getCouponInfoList())) {
                ArrayList<CouponInfoVO> couponInfos = new ArrayList<>();
                List<CouponInfoDTO> couponInfoDTOList = JSONArray.parseArray(mkShopRechargeDetail.getCouponInfoList()).toJavaList(CouponInfoDTO.class);
                Set<Long> couponIdList = couponInfoDTOList.stream().map(CouponInfoDTO::getId).collect(Collectors.toSet());
                if (!couponIdList.isEmpty()) {
                    Map<Long, ShopCoupon> couponMap = shopCouponService.list(new QueryWrapper().in(ShopCoupon::getId, couponIdList)).stream().collect(Collectors.toMap(ShopCoupon::getId, v -> v));
                    couponInfoDTOList.forEach(item -> {
                        ShopCoupon shopCoupon = couponMap.get(item.getId());

                        if (shopCoupon == null) {
                            return;
                        }
                        shopCoupon.setInfo();
                        couponInfos.add(new CouponInfoVO().setCoupon(shopCoupon).setNum(item.getNum()));
                    });
                }
                detailVO.setCouponInfoList(couponInfos);
            }
            return detailVO;
        }).collect(Collectors.toList());

        shopRechargeVO.setRechargeDetailList(detailList);

        return shopRechargeVO;
    }

    @Override
    public Boolean edit(Long shopId, MkShopRechargeDTO shopRechargeDTO) {
        MkShopRecharge shopRecharge = getOne(new QueryWrapper().eq(MkShopRecharge::getMasterShopId, shopId));
        BeanUtil.copyProperties(shopRechargeDTO, shopRecharge);
        if (shopRechargeDTO.getShopIdList() != null && !shopRechargeDTO.getShopIdList().isEmpty()) {
            shopRecharge.setShopIdList(JSONArray.toJSONString(shopRechargeDTO.getShopIdList()));
        }

        if (shopRechargeDTO.getRechargeDetailList() != null) {
            shopRechargeDetailService.remove(new QueryWrapper().eq(MkShopRechargeDetail::getShopRechargeId, shopRecharge.getId()));
            shopRechargeDTO.getRechargeDetailList().forEach(item -> {
                MkShopRechargeDetail mkShopRechargeDetail = BeanUtil.copyProperties(item, MkShopRechargeDetail.class, "couponInfoList");
                mkShopRechargeDetail.setCouponInfoList(JSONArray.toJSONString(item.getCouponInfoList()));
                mkShopRechargeDetail.setShopRechargeId(shopRecharge.getId());
                shopRechargeDetailService.save(mkShopRechargeDetail);
            });
        }
        shopConfigService.editStatusByShopIdList(shopId, shopRechargeDTO.getIsEnable(), false, "is_account_pay",
                shopRechargeDTO.getUseType(), shopRechargeDTO.getShopIdList());


        return updateById(shopRecharge);
    }

    @Override
    public BigDecimal checkRecharge(Long mainShopId, Long shopId, Long userId, Long rechargeDetailId, BigDecimal money) {
        MkShopRechargeVO rechargeVO = detail(mainShopId);
        if (!Objects.equals(mainShopId, shopId) && "part".equals(rechargeVO.getUseType()) && !rechargeVO.getShopIdList().contains(shopId)) {
            throw new CzgException("充值失败 该店铺不在充值店铺列表");
        }

        if (rechargeDetailId == null) {
            if (rechargeVO.getIsCustom() == 0) {
                throw new CzgException("未开启自定义充值金额");
            }
            return money;
        } else {
            MkShopRechargeDetail rechargeDetail = shopRechargeDetailService.getOne(new QueryWrapper().eq(MkShopRechargeDetail::getShopRechargeId, rechargeVO.getId()).eq(MkShopRechargeDetail::getId, rechargeDetailId));
            AssertUtil.isNull(rechargeDetail, "充值金额不存在");
            return rechargeDetail.getAmount();
        }
    }

    @Override
    public void recharge(Long shopId, Long shopUserId, Long rechargeDetailId, BigDecimal amount, Long paymentId,
                         String payType, LocalDateTime payTime, ShopUserFlowBizEnum bizEnum, boolean isNoJoin, String operator) {
        log.info("充值回调, 用户id: {}, 金额: {}, rechargeDetailId: {}", shopUserId, amount, rechargeDetailId);
        ShopUser shopUser = shopUserService.getById(shopUserId);
        ShopUserMoneyEditDTO shopUserMoneyEditDTO = new ShopUserMoneyEditDTO()
                .setId(shopUserId)
                .setType(1)
                .setBizEnum(bizEnum)
                .setRelationId(paymentId);
        shopUserMoneyEditDTO.setMoney(amount);
        Long flowId = shopUserService.updateMoney(shopUserMoneyEditDTO);
        BigDecimal rechargeAmount = amount;
        BigDecimal giftAmount = BigDecimal.ZERO;
        BigDecimal giftPoints = BigDecimal.ZERO;
        Integer giftCoupon = 0;

        // 标准充值
        if (rechargeDetailId != null) {
            MkShopRechargeDetail rechargeDetail = shopRechargeDetailService.getById(rechargeDetailId);
            try {
                if (rechargeDetail != null) {
                    if (rechargeDetail.getRewardAmount() != null && rechargeDetail.getRewardAmount().compareTo(BigDecimal.ZERO) > 0) {
                        // 赠送金额
                        ShopUserMoneyEditDTO shopUserMoneyEditRewardDTO = new ShopUserMoneyEditDTO()
                                .setId(shopUserId)
                                .setType(1)
                                .setRechargeId(flowId)
                                .setBizEnum(ShopUserFlowBizEnum.AWARD_IN)
                                .setRelationId(paymentId);
                        shopUserMoneyEditRewardDTO.setMoney(rechargeDetail.getRewardAmount());
                        shopUserService.updateMoney(shopUserMoneyEditRewardDTO);
                        giftAmount = rechargeDetail.getRewardAmount();
                    }
                    // 发放积分
                    if (rechargeDetail.getRewardPoints() != null && rechargeDetail.getRewardPoints() > 0) {
                        giftPoints = BigDecimal.valueOf(rechargeDetail.getRewardPoints());
                        pointsUserService.alterPoints(null, shopUserId, shopUser.getMainShopId(), PointsConstant.ADD, rechargeDetail.getRewardPoints(), flowId, "充值赠送");
                    }
                    // 发放优惠券
                    if (StrUtil.isNotBlank(rechargeDetail.getCouponInfoList())) {
                        List<CouponInfoDTO> list = JSONArray.parseArray(rechargeDetail.getCouponInfoList()).toJavaList(CouponInfoDTO.class);
                        for (CouponInfoDTO item : list) {
                            if (item.getId() == null) {
                                return;
                            }
                            giftCoupon = giftCoupon + item.getNum();
                            shopCouponRecordService.grant(shopId, new MkRewardCouponDTO().setCouponId(item.getId())
                                    .setNum(item.getNum())
                                    .setUserId(shopUser.getUserId())
                                    .setShopId(shopId), "充值赠券");
                        }

                    }
                }
            } catch (Exception e) {
                log.error("recharge充值有问题", e);
            }
        }
        BigDecimal finalRechargeAmount = rechargeAmount;
        BigDecimal finalGiftAmount = giftAmount;
        BigDecimal finalGiftPoints = giftPoints;
        Integer finalGiftCoupon = giftCoupon;
        FunUtils.asyncSafeRunVoid(() -> {
            RechargePrintDTO rechargePrintDTO = new RechargePrintDTO();
            rechargePrintDTO.setUserId(shopUserId);
            rechargePrintDTO.setUserName(shopUser.getNickName());
            rechargePrintDTO.setUserPhone(DesensitizedUtil.mobilePhone(shopUser.getPhone()));
            rechargePrintDTO.setPayTime(payTime);
            rechargePrintDTO.setRechargeAmount(finalRechargeAmount);
            rechargePrintDTO.setGiftAmount(finalGiftAmount);
            rechargePrintDTO.setGiftPoints(finalGiftPoints);
            rechargePrintDTO.setGiftCoupon(finalGiftCoupon);
            rechargePrintDTO.setPayType(bizEnum.getMsg());
            rechargePrintDTO.setBalance(shopUser.getAmount().add(finalRechargeAmount).add(finalGiftAmount));
            rechargePrintDTO.setRechargeId(flowId.toString());
            rechargePrintDTO.setOperator(operator);
            rabbitPublisher.sendOtherPrintMsg(shopId, rechargePrintDTO, "RECHARGE");
        });
        FunUtils.asyncSafeRunVoid(() -> {
            memberConfigService.deliver(shopUser, TableValueConstant.MemberExpFlow.Type.RECHARGE, amount, null, rechargeDetailId);
        });
        if (isNoJoin) {
            FunUtils.asyncSafeRunVoid(() -> memberConfigService.joinMemberByCondition(shopId, shopUser.getUserId(), shopUser));
        }
    }

    @Override
    public List<RechargeListVO> getList(long loginIdAsLong) {
        ArrayList<RechargeListVO> rechargeListVOS = new ArrayList<>();
        List<ShopUser> shopUserList = shopUserService.list(new QueryWrapper().eq(ShopUser::getUserId, loginIdAsLong).isNotNull(ShopUser::getAmount));
        if (shopUserList.isEmpty()) {
            return rechargeListVOS;
        }
        Set<Long> shopIdList = shopUserList.stream().map(ShopUser::getSourceShopId).collect(Collectors.toSet());
        Map<Long, ShopInfo> shopInfoMap = shopInfoService.list(new QueryWrapper().in(ShopInfo::getId, shopIdList)).stream().collect(Collectors.toMap(ShopInfo::getId, item -> item));
        shopUserList.forEach(item -> {
            ShopInfo shopInfo = shopInfoMap.get(item.getSourceShopId());
            if (shopInfo == null) {
                return;
            }
            rechargeListVOS.add(new RechargeListVO().setShopName(shopInfo.getShopName())
                    .setShopId(shopInfo.getId())
                    .setAmount(item.getAmount())
                    .setLogo(shopInfo.getLogo()));
        });
        return rechargeListVOS;
    }

    @Override
    public List<MkShopRechargeShopListVO> shopList(Long shopId, String shopName) {
        Long mainShopId = shopInfoService.getMainIdByShopId(shopId);
        MkShopRechargeVO rechargeVO = detail(shopId);
        List<Long> shopIdList = rechargeVO.getShopIdList();

        QueryWrapper queryWrapper = new QueryWrapper().like(ShopInfo::getShopName, shopName);
        if ("all".equals(rechargeVO.getUseType())) {
            queryWrapper.and(q -> {
                q.eq(ShopInfo::getMainId, mainShopId).or(q1 -> {
                    q1.eq(ShopInfo::getId, mainShopId);
                });
            });
            shopIdList = null;
        } else {
            shopIdList.add(shopId);
            queryWrapper.in(ShopInfo::getId, shopIdList);
        }


        return shopInfoService.getByMainIdOrList(mainShopId, shopIdList, shopName)
                .stream()
                .map(shop -> BeanUtil.copyProperties(shop, MkShopRechargeShopListVO.class))
                .toList();
    }
}
