package com.ym.mec.biz.service.impl;

import com.ym.mec.auth.api.entity.SysUser;
import com.ym.mec.biz.dal.dao.*;
import com.ym.mec.biz.dal.dto.*;
import com.ym.mec.biz.dal.entity.*;
import com.ym.mec.biz.dal.enums.*;
import com.ym.mec.biz.dal.page.ChildrenReserveQueryInfo;
import com.ym.mec.biz.service.*;
import com.ym.mec.common.dal.BaseDAO;
import com.ym.mec.common.exception.BizException;
import com.ym.mec.common.page.PageInfo;
import com.ym.mec.common.service.IdGeneratorService;
import com.ym.mec.common.service.impl.BaseServiceImpl;
import com.ym.mec.common.tenant.TenantContextHolder;
import com.ym.mec.thirdparty.message.MessageSenderPluginContext;
import com.ym.mec.util.collection.MapUtil;
import com.ym.mec.util.date.DateUtil;
import com.ym.mec.util.http.HttpUtil;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Service
public class ChildrenDayReserveServiceImpl extends BaseServiceImpl<Integer, ChildrenDayReserve> implements ChildrenDayReserveService {

    @Autowired
    private ChildrenDayReserveDao childrenDayReserveDao;
    @Autowired
    private MusicGroupDao musicGroupDao;
    @Autowired
    private DegreeLevelFeeDao degreeLevelFeeDao;
    @Autowired
    private OrganizationDegreeCourseFeeDao organizationDegreeCourseFeeDao;
    @Autowired
    private StudentDao studentDao;
    @Autowired
    private ChildrenDayDegreeDetailService childrenDayDegreeDetailService;
    @Autowired
    private StudentPaymentOrderService studentPaymentOrderService;
    @Autowired
    private SysConfigDao sysConfigDao;
    @Autowired
    private IdGeneratorService idGeneratorService;
    @Autowired
    private StudentPaymentRouteOrderService studentPaymentRouteOrderService;
    @Autowired
    private PayService payService;
    @Autowired
    private SysUserCashAccountDetailService sysUserCashAccountDetailService;
    @Autowired
    private ContractService contractService;
    @Autowired
    private SysUserCashAccountService sysUserCashAccountService;
    @Autowired
    private SysMessageService sysMessageService;
    @Autowired
    private StudentRegistrationService studentRegistrationService;
    @Autowired
    private ChildrenDayDegreeInfoService childrenDayDegreeInfoService;

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public BaseDAO<Integer, ChildrenDayReserve> getDAO() {
        return childrenDayReserveDao;
    }

    @Override
    public ChildrenDayReserve addReserve(SysUser user, YesOrNoEnum status) {
        Date nowTime = new Date();
        String startTimeStr = sysConfigDao.findConfigValue("children_day_start_time");
        Date startTime = DateUtil.stringToDate(startTimeStr);
        if (startTime.compareTo(nowTime) > 0) {
            throw new BizException("活动还未开始，谢谢关注");
        }
        String endTimeStr = sysConfigDao.findConfigValue("children_day_end_time");
        Date endTime = DateUtil.stringToDate(endTimeStr);
        if (endTime.compareTo(nowTime) <= 0) {
            throw new BizException("活动已结束，谢谢关注");
        }

        ChildrenDayReserve hasOne = childrenDayReserveDao.getByUserId(user.getId());
        if (hasOne != null) {
            throw new BizException("您已预报名");
        }
        studentDao.lockUser(user.getId());
        MusicGroup studentLastMusicGroup = musicGroupDao.getStudentLastMusicGroup(user.getId());
        ChildrenDayReserve childrenDayReserve = new ChildrenDayReserve();
        childrenDayReserve.setUserId(user.getId());
        childrenDayReserve.setOrganId(user.getOrganId());
        childrenDayReserve.setIsReserve(status);
        if (studentLastMusicGroup != null) {
            childrenDayReserve.setCooperationOrganId(studentLastMusicGroup.getCooperationOrganId());
            childrenDayReserve.setMusicGroupId(studentLastMusicGroup.getId());
        }
        childrenDayReserveDao.insert(childrenDayReserve);
        return childrenDayReserve;
    }

    @Override
    public ChildrenDayReserve getUserReserve(Integer userId) {
        return childrenDayReserveDao.getByUserId(userId);
    }

    @Override
    public PageInfo<ChildrenStatisticsDto> queryStatisticsPage(ChildrenReserveQueryInfo queryInfo) {
        PageInfo<ChildrenStatisticsDto> pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
        Map<String, Object> params = new HashMap<String, Object>();
        MapUtil.populateMap(params, queryInfo);

        List<ChildrenStatisticsDto> dataList = null;
        int count = childrenDayReserveDao.countChildrenStatistics(params);
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            dataList = childrenDayReserveDao.queryChildrenStatistics(params);
            //统计
            pageInfo.setStatInfo(childrenDayReserveDao.childrenStatistics(params));
            //获取乐团在读人数
            List<String> musicGroupIds = dataList.stream().map(e -> e.getMusicGroupId()).collect(Collectors.toList());
            Map<String, Long> normalNum = MapUtil.convertIntegerMap(studentRegistrationService.countNormalNum(musicGroupIds));
            dataList.forEach(e -> {
                e.setNormalNum(normalNum.get(e.getMusicGroupId()) == null ? 0 : normalNum.get(e.getMusicGroupId()).intValue());
            });
        }
        if (count == 0) {
            dataList = new ArrayList<>();
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }

    @Override
    public DegreeFeeDto getFeeInfo(Integer organId) {
        List<DegreeLevelFee> degreeLevelFee = degreeLevelFeeDao.getAll(TenantContextHolder.getTenantId());
        OrganizationDegreeCourseFee courseFee = organizationDegreeCourseFeeDao.getByOrganId(organId);
        DegreeFeeDto degreeFeeDto = new DegreeFeeDto();
        degreeFeeDto.setDegreeLevelFee(degreeLevelFee);
        degreeFeeDto.setOrganizationDegreeCourseFee(courseFee);
        return degreeFeeDto;
    }

    @Override
    public PageInfo<ChildrenStatisticsDetailDto> queryStatisticsDetailPage(ChildrenReserveQueryInfo queryInfo) {
        PageInfo<ChildrenStatisticsDetailDto> pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
        Map<String, Object> params = new HashMap<String, Object>();
        MapUtil.populateMap(params, queryInfo);

        List<ChildrenStatisticsDetailDto> dataList = null;
        int count = childrenDayReserveDao.countChildrenStatisticsDetail(params);
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            dataList = childrenDayReserveDao.queryChildrenStatisticsDetail(params);
        }
        if (count == 0) {
            dataList = new ArrayList<>();
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map pay(DegreePayDto degreePayDto) throws Exception {
        studentDao.lockUser(degreePayDto.getUserId());

        //检查进行中的订单
        List<ChildrenDayDegreeDetail> details = childrenDayDegreeDetailService.getByUserIdAndStatus(degreePayDto.getUserId(), 1);

        //处理关闭订单
        if (details.size() > 0 && degreePayDto.getRePay()) {
            Long orderId = details.get(0).getOrderId();
            StudentPaymentOrder oldOrder = studentPaymentOrderService.get(orderId);
            if (oldOrder != null) {
                oldOrder.setStatus(DealStatusEnum.FAILED);
                oldOrder.setMemo("用户手动关闭");
                studentPaymentOrderService.callOrderCallBack(oldOrder);
            }
        }

        //获取课程的价格
        OrganizationDegreeCourseFee courseFee = organizationDegreeCourseFeeDao.getByOrganId(degreePayDto.getOrganId());

        //检查已经购买的课程（不能重复）
        details = childrenDayDegreeDetailService.getByUserIdAndStatus(degreePayDto.getUserId(), 2);
        Set<Integer> detailTypes = details.stream().map(ChildrenDayDegreeDetail::getType).collect(Collectors.toSet());

        if (degreePayDto.getTheoryLevel() != null && degreePayDto.getTheoryLevel() > 0 &&
                courseFee != null && !degreePayDto.getTheoryCourse() && !detailTypes.contains(5)) {
            throw new BizException("参加乐理考试，请选乐理考级专项训练课");
        }

        if (degreePayDto.getVip1v1() && detailTypes.contains(3)) {
            throw new BizException("您已购买过VIP 1V1课程，请勿重复选择");
        }

        if (degreePayDto.getVip1v2() && detailTypes.contains(4)) {
            throw new BizException("您已购买过VIP 1V2课程，请勿重复选择");
        }

        if (degreePayDto.getTheoryCourse() && detailTypes.contains(5)) {
            throw new BizException("您已购买过乐理考级专项训练课，请勿重复选择");
        }
        //检查器乐考级等级是否重复
        if (degreePayDto.getMusicGradeLevel() != null && degreePayDto.getMusicGradeLevel() > 0) {
            if (degreePayDto.getSubject() == null) {
                throw new BizException("请选择考级声部");
            }
            List<ChildrenDayDegreeDetail> hasList = details.stream().filter(e -> e.getType().equals(1)).filter(e -> e.getSubjectId().equals(degreePayDto.getSubject()))
                    .filter(e -> e.getLevel().equals(degreePayDto.getMusicGradeLevel())).collect(Collectors.toList());
            if (hasList.size() > 0) {
                throw new BizException("您已报考同声部同等级，请勿重复报考");
            }

            if (degreePayDto.getNeedVipCourse() && !detailTypes.contains(3) && !detailTypes.contains(4)) {
                throw new BizException("请选择VIP课程");
            }
        }

        //检查乐理考级等级是否重复
        if (degreePayDto.getTheoryLevel() != null && degreePayDto.getTheoryLevel() > 0) {
            List<ChildrenDayDegreeDetail> hasList = details.stream().filter(e -> e.getType().equals(2))
                    .filter(e -> e.getLevel().equals(degreePayDto.getTheoryLevel())).collect(Collectors.toList());
            if (hasList.size() > 0) {
                throw new BizException("您已报考同等级乐理，请勿重复报考");
            }
        }

        ChildrenDayReserve userReserve = this.getUserReserve(degreePayDto.getUserId());

        if (userReserve == null) {
            userReserve = this.addReserve(degreePayDto.getUser(), YesOrNoEnum.NO);
        }
        degreePayDto.setReserveId(userReserve.getId());

        List<DegreeLevelFee> degreeLevelFees = degreeLevelFeeDao.getAll(TenantContextHolder.getTenantId());

        //订单总金额
        BigDecimal orderAmount = BigDecimal.ZERO;

        //器乐考级费用
        BigDecimal gradeFee = BigDecimal.ZERO;
        if (degreePayDto.getMusicGradeLevel() != null && degreePayDto.getMusicGradeLevel() > 0) {
            gradeFee = degreeLevelFees.stream().filter(e -> e.getLevel().equals(degreePayDto.getMusicGradeLevel())).map(DegreeLevelFee::getGrade).reduce(BigDecimal.ZERO, BigDecimal::add);
            orderAmount = orderAmount.add(gradeFee);
        }

        //乐理考级费用
        BigDecimal theoryLevelFee = BigDecimal.ZERO;
        if (degreePayDto.getTheoryLevel() != null && degreePayDto.getTheoryLevel() > 0) {
            theoryLevelFee = degreeLevelFees.stream().filter(e -> e.getLevel().equals(degreePayDto.getTheoryLevel())).map(DegreeLevelFee::getTheory).reduce(BigDecimal.ZERO, BigDecimal::add);
            orderAmount = orderAmount.add(theoryLevelFee);
        }

        //VIP 1v1 1v2费用
        BigDecimal vip1v1Fee = BigDecimal.ZERO;
        if (degreePayDto.getVip1v1()) {
            vip1v1Fee = courseFee.getVip1v1();
            orderAmount = orderAmount.add(vip1v1Fee);
        }

        //VIP 1v2费用
        BigDecimal vip1v2Fee = BigDecimal.ZERO;
        if (degreePayDto.getVip1v2()) {
            vip1v2Fee = courseFee.getVip1v2();
            orderAmount = orderAmount.add(vip1v2Fee);
        }

        //乐理课费用
        BigDecimal theoryCourseFee = BigDecimal.ZERO;
        if (degreePayDto.getTheoryCourse()) {
            theoryCourseFee = courseFee.getTheory();
            orderAmount = orderAmount.add(theoryCourseFee);
        }

        if (orderAmount.compareTo(degreePayDto.getAmount()) != 0) {
            throw new BizException("价格不符，请核查");
        }


        String channelType = "";
        Date nowDate = new Date();

        BigDecimal balance = BigDecimal.ZERO;

        String orderNo = idGeneratorService.generatorId("payment") + "";
        StudentPaymentOrder studentPaymentOrder = new StudentPaymentOrder();

        studentPaymentOrder.setPaymentChannel("BALANCE");
        studentPaymentOrder.setUserId(degreePayDto.getUserId());
        studentPaymentOrder.setGroupType(GroupType.DEGREE);
        studentPaymentOrder.setOrderNo(orderNo);
        studentPaymentOrder.setType(OrderTypeEnum.DEGREE);
        studentPaymentOrder.setExpectAmount(orderAmount);
        studentPaymentOrder.setActualAmount(orderAmount);
        studentPaymentOrder.setBalancePaymentAmount(balance);
        studentPaymentOrder.setStatus(DealStatusEnum.ING);
        studentPaymentOrder.setRemitFee(BigDecimal.ZERO);
        studentPaymentOrder.setCourseRemitFee(BigDecimal.ZERO);
        studentPaymentOrder.setOrganId(degreePayDto.getOrganId());
        studentPaymentOrder.setRoutingOrganId(degreePayDto.getOrganId());
        studentPaymentOrder.setCreateTime(nowDate);
        studentPaymentOrder.setUpdateTime(nowDate);
        studentPaymentOrderService.insert(studentPaymentOrder);

        //添加购买详情
        degreePayDto.setOrderId(studentPaymentOrder.getId());
        childrenDayDegreeDetailService.addOrderDetails(degreePayDto, gradeFee, theoryLevelFee, vip1v1Fee, vip1v2Fee, theoryCourseFee);
        childrenDayDegreeDetailService.addDegreeDetails(degreePayDto, gradeFee, theoryLevelFee, vip1v1Fee, vip1v2Fee, theoryCourseFee);

        studentPaymentOrder.setVersion(0);
        if (orderAmount.compareTo(BigDecimal.ZERO) == 0) {
            studentPaymentRouteOrderService.addRouteOrder(orderNo, degreePayDto.getOrganId(), balance);
            Map<String, String> notifyMap = new HashMap<>();
            notifyMap.put("tradeState", "1");
            notifyMap.put("merOrderNo", studentPaymentOrder.getOrderNo());
            notifyMap.put("channelType", channelType);
            notifyMap.put("orderNo", "");
            studentPaymentOrderService.updateOrder(notifyMap);
            return notifyMap;
        }

        String orderSubject = "6.1考级活动";
        String receiver = "degree";

        String baseApiUrl = sysConfigDao.findConfigValue("base_api_url");

        Map payMap = payService.getPayMap(
                orderAmount,
                balance,
                orderNo,
                baseApiUrl + "/api-student/studentOrder/callback",
                baseApiUrl + "/api-student/studentOrder/paymentResult?orderNo=" + orderNo,
                orderSubject,
                orderSubject,
                degreePayDto.getOrganId(),
                receiver
        );

        studentPaymentOrder.setOrganId(degreePayDto.getOrganId());
        studentPaymentOrder.setMerNos((String) payMap.get("routingMerNos"));
        studentPaymentOrder.setPaymentChannel((String) payMap.get("type"));
        studentPaymentOrder.setUpdateTime(nowDate);
        studentPaymentOrderService.update(studentPaymentOrder);
        return payMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean orderCallback(StudentPaymentOrder studentPaymentOrder) {
        Date nowDate = new Date();
        //更新订单信息
        studentPaymentOrder.setUpdateTime(nowDate);
        int updateCount = studentPaymentOrderService.update(studentPaymentOrder);
        if (updateCount <= 0) {
            throw new BizException("订单更新失败");
        }

        Integer userId = studentPaymentOrder.getUserId();

        Map<Integer, String> map = new HashMap<>();
        map.put(userId, userId.toString());

        //支付成功
        if (studentPaymentOrder.getStatus() == DealStatusEnum.SUCCESS) {
            if (childrenDayDegreeDetailService.updateStatus(studentPaymentOrder.getId(), 2) <= 0) {
                throw new BizException("购买详情，更新失败");
            }
            childrenDayDegreeInfoService.addInfo(studentPaymentOrder.getId());

            //插入交易明细
            BigDecimal amount = studentPaymentOrder.getActualAmount();
            if (amount.compareTo(BigDecimal.ZERO) > 0) {
                SysUserCashAccount cashAccount = sysUserCashAccountService.get(userId);
                //充值
                SysUserCashAccountDetail rechargeDetail = new SysUserCashAccountDetail();
                rechargeDetail.setAmount(amount);
                rechargeDetail.setBalance(cashAccount.getBalance().add(amount));
                rechargeDetail.setComment("缴费前充值");
                rechargeDetail.setCreateTime(nowDate);
                rechargeDetail.setStatus(DealStatusEnum.SUCCESS);
                rechargeDetail.setTransNo(studentPaymentOrder.getTransNo());
                rechargeDetail.setType(PlatformCashAccountDetailTypeEnum.RECHARGE);
                rechargeDetail.setUpdateTime(nowDate);
                rechargeDetail.setUserId(userId);
                rechargeDetail.setChannel(studentPaymentOrder.getPaymentChannel());
                rechargeDetail.setComAmount(studentPaymentOrder.getComAmount());
                rechargeDetail.setPerAmount(studentPaymentOrder.getPerAmount());
                sysUserCashAccountDetailService.insert(rechargeDetail);

                //缴费
                SysUserCashAccountDetail paymentDetail = new SysUserCashAccountDetail();
                paymentDetail.setAmount(amount.negate());
                paymentDetail.setBalance(cashAccount.getBalance());
                paymentDetail.setComment("6.1考级活动");
                paymentDetail.setCreateTime(nowDate);
                paymentDetail.setStatus(DealStatusEnum.SUCCESS);
                paymentDetail.setTransNo(studentPaymentOrder.getTransNo());
                paymentDetail.setType(PlatformCashAccountDetailTypeEnum.PAY_FEE);
                paymentDetail.setUpdateTime(nowDate);
                paymentDetail.setUserId(userId);
                rechargeDetail.setChannel(studentPaymentOrder.getPaymentChannel());
                sysUserCashAccountDetailService.insert(paymentDetail);
            }

            try {
                contractService.transferProduceContract(userId, null, null);
            } catch (Exception e) {
                logger.error("产品协议生成失败", e);
            }
            return true;
        }

        if (studentPaymentOrder.getStatus() == DealStatusEnum.CLOSE || studentPaymentOrder.getStatus() == DealStatusEnum.FAILED) {
            if (childrenDayDegreeDetailService.updateStatus(studentPaymentOrder.getId(), 3) <= 0) {
                throw new BizException("购买详情，更新失败");
            }

            if (studentPaymentOrder.getBalancePaymentAmount() != null && studentPaymentOrder.getBalancePaymentAmount().compareTo(BigDecimal.ZERO) > 0) {
                sysUserCashAccountService.updateBalance(studentPaymentOrder.getUserId(), studentPaymentOrder.getBalancePaymentAmount(), PlatformCashAccountDetailTypeEnum.REFUNDS, "6.1考级支付失败");
            }

            sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, MessageTypeEnum.STUDENT_SMS_PUSH_SPORADIC_PAYMENT_FAILED, map, null, 0, null, "STUDENT",
                    studentPaymentOrder.getActualAmount(), "6.1考级活动");
            return false;
        }
        return true;
    }

    @Override
    public List<ChildrenStatisticsDetailDto> exportStatisticsDetail(ChildrenReserveQueryInfo queryInfo) {
        return childrenDayReserveDao.exportStatisticsDetail(queryInfo);
    }

    @Override
    public Boolean sendMsg(List<ChildrenDayMsgDto> msgDtos) {
        List<ChildrenStatisticsDetailDto> reserves = childrenDayReserveDao.getSendMsgList(msgDtos);
        if (reserves.size() <= 0) {
            return true;
        }

        Map<Integer, String> userMap = new HashMap<>();
        Map<Integer, String> userPhoneMap = new HashMap<>();

        List<Integer> userList = reserves.stream().map(ChildrenStatisticsDetailDto::getUserId).collect(Collectors.toList());
        List<Integer> payedUserIds = childrenDayDegreeInfoService.getPayedUser(userList);

        for (ChildrenStatisticsDetailDto reserve : reserves) {
            if (payedUserIds.contains(reserve.getUserId())) {
                userList.remove(reserve.getUserId());
                continue;
            }
            userMap.put(reserve.getUserId(), reserve.getUserId().toString());
            userPhoneMap.put(reserve.getUserId(), reserve.getPhone());
        }

        if (userMap.size() <= 0) {
            return true;
        }

        String baseApiUrl = sysConfigDao.findConfigValue("base_api_url");
        String pushUrl = baseApiUrl + "/#/childrenPayment?dHlwZT0x";

        sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, MessageTypeEnum.CHILDREN_DAY_NOTICE_PUSH,
                userMap, null, 0, "5?" + pushUrl, "STUDENT");

        sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.AWSMS, MessageTypeEnum.CHILDREN_DAY_NOTICE_MSG,
                userPhoneMap, null, 0, null, null, HttpUtil.getSortUrl(pushUrl));

        childrenDayReserveDao.updateSendMasStatus(userList);
        return true;
    }
}
