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

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ym.mec.biz.dal.dao.TenantAssetsInfoDao;
import com.ym.mec.biz.dal.entity.*;
import com.ym.mec.biz.dal.entity.TenantConfigDetail.CloudRoomRule;
import com.ym.mec.biz.dal.enums.TeachModeEnum;
import com.ym.mec.biz.dal.vo.TenantCloudCourseRecordVo;
import com.ym.mec.biz.service.*;
import com.ym.mec.common.exception.BizException;
import com.ym.mec.common.page.WrapperUtil;
import com.ym.mec.thirdparty.message.MessageSenderPluginContext;
import com.ym.mec.thirdparty.yqpay.DateUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.text.ParseException;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static com.ym.mec.biz.dal.enums.MessageTypeEnum.*;

/**
 * 机构资产信息(TenantAssetsInfo)表服务实现类
 *
 * @author hgw
 * @since 2021-12-22 14:27:02
 */
@Service("tenantAssetsInfoService")
public class TenantAssetsInfoServiceImpl extends ServiceImpl<TenantAssetsInfoDao, TenantAssetsInfo> implements TenantAssetsInfoService {

    private final static Logger log = LoggerFactory.getLogger(TenantAssetsInfoServiceImpl.class);

    @Autowired
    private TenantConfigService tenantConfigService;
    @Autowired
    private TenantCloudCourseRecordService tenantCloudCourseRecordService;
    @Autowired
    private TenantInfoService tenantInfoService;
    @Autowired
    private SysMessageService sysMessageService;

    private final Predicate<String> VipPredicate = (o) -> Objects.equals(o, CourseSchedule.CourseScheduleType.VIP.getCode());

    private final Predicate<String> PracticePredicate = (o) -> Objects.equals(o, CourseSchedule.CourseScheduleType.PRACTICE.getCode());

    /**
     * 手动执行未扣费的流水
     *
     * @param ids 流水表id集合
     */
    public void manualDeductAmount(List<Integer> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        List<TenantCloudCourseRecordVo> records = tenantCloudCourseRecordService.queryList(new HashMap<String, Object>() {{
            put("ids", ids);
            put("deductState", 3);
        }});
        if (CollectionUtils.isEmpty(records)) {
            return;
        }
        records.forEach(record -> {
            log.info("manualDeductAmount >>>>>> {}", record);
            //获取总人数 (算上老师
            String totalPeople = getTotalPeople(record.getType(), record.getCourseId());
            //获取云教室规则  String人数-BigDecimal每分钟扣费标准
            Map<String, BigDecimal> rule = tenantConfigService.getRule(record.getTenantId());
            if (Objects.isNull(rule)) {
                throw new BizException("未查询到该机构的课程收费标准!");
            }
            //获取每分钟扣费标准
            BigDecimal minutePrice = rule.get(String.valueOf(totalPeople));
            if (Objects.isNull(minutePrice)) {
                throw new BizException("未查到 [" + totalPeople + "人(含老师)] 的课程收费标准!");
            }
            //计算总上课时间
            BigDecimal courseDate = getCourseDate(record.getClassDate(), record.getStartClassTime(), record.getEndClassTime());
            //课程总价 = 每分钟扣费标准 * 总上课时间
            BigDecimal coursePrice = minutePrice.multiply(courseDate, new MathContext(3, RoundingMode.HALF_UP));
            //修改流水
            TenantCloudCourseRecord sourceCord = new TenantCloudCourseRecord();
            sourceCord.setId(record.getId());
            sourceCord.setAmount(coursePrice);
            sourceCord.setDeductState(1);
            tenantCloudCourseRecordService.updateById(sourceCord);
            //扣余额
            if (baseMapper.checkDeductAmount(coursePrice, record.getTenantId()) == 0) {
                throw new BizException("余额不足!");
            }
        });

    }

    /**
     * 上课时间到了后扣除账户余额，可以为负数
     *
     * @param dto 课程集合
     */
    public void deductAmount(List<CourseSchedule> dto) {
        //校验课程 筛选出线上课
        dto = getOnlineCourse(dto);
        if (CollectionUtils.isEmpty(dto)) {
            log.info("deductAmount >>>>>>courseData is null {}", dto);
            return;
        }
        //开始扣费
        dto.forEach(course -> {
            log.info("deductAmount >>>>>> {}", course);
            if (Objects.isNull(course.getTenantId())) {
                log.info("deductAmount getTenantId is null>>>>>> {}", course);
                return;
            }
            //获取总人数 ，+1算上老师
            String totalPeople = getTotalPeople(course.getType().getCode(), course.getId().intValue());
            //获取云教室规则  String人数-BigDecimal每分钟扣费标准
            Map<String, BigDecimal> rule = tenantConfigService.getRule(course.getTenantId());
            if (Objects.isNull(rule)) {
                //没有这个扣费标准,写未扣费流水记录
                insertRecord(course, BigDecimal.ZERO, 3);
                return;
            }
            //获取每分钟扣费标准
            BigDecimal minutePrice = rule.get(totalPeople);
            if (Objects.isNull(minutePrice)) {
                //没有这个扣费标准,写未扣费流水记录
                insertRecord(course, BigDecimal.ZERO, 3);
                return;
            }
            //计算总上课时间
            BigDecimal courseDate = getCourseDate(course.getClassDate(), course.getStartClassTime(), course.getEndClassTime());
            //课程总价 = 每分钟扣费标准 * 总上课时间
            BigDecimal coursePrice = minutePrice.multiply(courseDate, new MathContext(3, RoundingMode.HALF_UP));
            //写流水
            insertRecord(course, coursePrice, 1);
            log.info("deductAmount >>>>>> coursePrice {}  tenantId {}", coursePrice, course.getTenantId());
            //查询余额信息
            TenantAssetsInfo assetsInfo = baseMapper.selectOne(new QueryWrapper<TenantAssetsInfo>()
                    .eq("tenant_id_", course.getTenantId()));
            if (Objects.nonNull(assetsInfo)) {
                //扣余额
                baseMapper.deductAmount(coursePrice, course.getTenantId());
                //当前余额
                BigDecimal balance = assetsInfo.getBalance();
                //当前余额 减 本次扣费 的剩余额度
                BigDecimal after = balance.subtract(coursePrice).setScale(2, RoundingMode.HALF_UP);
                //判断是否发送信息
                sendMsg(course.getTenantId(), balance, after);
            }
        });

    }

    /**
     * 判断是否发送信息
     * <p>
     * 1.判断扣费前余额大于300并在本次扣费后剩余额度小于300就发信息提醒
     * <p>
     * 2.当前扣费为负数后要发邮件和短信提醒
     *
     * @param tenantId 机构id
     * @param balance  扣费前金额
     * @param after    扣费后金额
     */
    private void sendMsg(Integer tenantId, BigDecimal balance, BigDecimal after) {
        TenantInfo t = tenantInfoService.getById(tenantId);
        //当前余额大于300 并且 本次扣除后剩余额度小于300
        BigDecimal threeHundred = new BigDecimal(300);
        if (balance.compareTo(threeHundred) >= 0 && after.compareTo(threeHundred) < 0) {

            //邮件
            if (StringUtils.isNotBlank(t.getEmail())) {
                //机构名称 300 余额
                Object[] emailMsg = {t.getName(), 300, after.toString()};
                Map<Integer, String> sendPar = new HashMap<>();
                sendPar.put(t.getUserId(), t.getEmail());
                log.info("platformSendToAll>>> 余额不足 receiveUserId {} email {} objs {} sendPar {}", t.getUserId(), t.getEmail(), emailMsg, sendPar);
                sysMessageService.batchSendMessage(-1, MessageSenderPluginContext.MessageSender.EMAIL,
                        EMAIL_TENANT_INSUFFICIENT_BALANCE,
                        sendPar, null, 0, null,
                        "SYSTEM", emailMsg);
            }

            //短信
            if (StringUtils.isNotBlank(t.getPhone())) {
                //机构名称
                Object[] phoneMsg = {t.getName()};
                Map<Integer, String> sendPar2 = new HashMap<>();
                sendPar2.put(t.getUserId(), t.getPhone());
                log.info("platformSendToAll>>> 余额不足 receiveUserId {} phone {} objs {} sendPar {}", t.getUserId(), t.getPhone(), phoneMsg, sendPar2);
                sysMessageService.batchSendMessage(-1, MessageSenderPluginContext.MessageSender.AWSMS,
                        SMS_TENANT_INSUFFICIENT_BALANCE,
                        sendPar2, null, 0, null,
                        "SYSTEM", phoneMsg);
            }
        }

        //当前余额已经扣为负数
        if (after.compareTo(BigDecimal.ZERO) < 0) {
            //邮件
            if (StringUtils.isNotBlank(t.getEmail())) {
                //机构名称 余额
                Object[] emailMsg = {t.getName(), after.toString()};
                Map<Integer, String> sendPar = new HashMap<>();
                sendPar.put(t.getUserId(), t.getEmail());
                log.info("platformSendToAll>>> 已欠费 receiveUserId {} email {} objs {} sendPar {}", t.getUserId(), t.getEmail(), emailMsg, sendPar);
                sysMessageService.batchSendMessage(-1, MessageSenderPluginContext.MessageSender.EMAIL,
                        EMAIL_TENANT_EXPIRE, sendPar, null, 0, null,
                        "SYSTEM", emailMsg);
            }

            //短信
            if (StringUtils.isNotBlank(t.getPhone())) {
                //机构名称
                Object[] phoneMsg = {t.getName()};
                Map<Integer, String> sendPar2 = new HashMap<>();
                sendPar2.put(t.getUserId(), t.getPhone());
                log.info("platformSendToAll>>> 已欠费 receiveUserId {} phone {} objs {} sendPar {}", t.getUserId(), t.getPhone(), phoneMsg, sendPar2);
                sysMessageService.batchSendMessage(-1, MessageSenderPluginContext.MessageSender.AWSMS,
                        SMS_TENANT_EXPIRE, sendPar2, null, 0, null,
                        "SYSTEM", phoneMsg);
            }
        }
    }

    /**
     * 写入流水
     *
     * @param course      课程
     * @param coursePrice 课程总价
     * @param state       扣费状态 1扣费  3未扣费
     */
    private void insertRecord(CourseSchedule course, BigDecimal coursePrice, Integer state) {
        log.info("deductAmount  insertRecord >>>>>> {}  coursePrice {} state {}", course, coursePrice, state);
        //写入流水
        TenantCloudCourseRecord record = new TenantCloudCourseRecord();
        record.setCourseId(course.getId().intValue());
        record.setTenantId(course.getTenantId());
        record.setAmount(coursePrice);
        record.setDeductState(state);
        record.setCreatedTime(new Date());
        tenantCloudCourseRecordService.save(record);
    }

    private List<CourseSchedule> getOnlineCourse(List<CourseSchedule> dto) {
        if (CollectionUtils.isEmpty(dto)) {
            return null;
        }
        return dto.stream()
                .filter(c -> c.getTeachMode().equals(TeachModeEnum.ONLINE))
                .filter(c -> VipPredicate.or(PracticePredicate).test(c.getType().getCode()))
                .collect(Collectors.toList());
    }

    private String getTotalPeople(String type, Integer courseId) {
        String result = "2";
        if (VipPredicate.test(type)) {
            result = Optional.ofNullable(courseId)
                    .map(baseMapper::queryPeopleNum)
                    .map(String::valueOf)
                    .orElse(result);
        }
        return result;
    }

    private BigDecimal getCourseDate(Date date, Date startTime, Date endTime) {
        String dateStr = DateUtils.formatDate(date, DateUtils.YYYY_MM_DD_DEF);
        int durationMinute = 0;
        try {
            Date startDT = getDateTime(dateStr, startTime);
            Date endDT = getDateTime(dateStr, endTime);
            //课程结束时间-课程开始时间
            long durationTime = endDT.getTime() - startDT.getTime();
            //相差多少分钟
            durationMinute = new Long(durationTime / 1000 / 60).intValue();
        } catch (ParseException e) {
            log.error("deductAmount >>>>>> 计算时间出错 : {} {} {}", date, startTime, endTime);
        }
        return BigDecimal.valueOf(durationMinute);
    }

    private Date getDateTime(String dateStr, Date time) throws ParseException {
        String timeStr = DateUtils.formatDate(time, "HH:mm:ss");
        String s = dateStr + " " + timeStr;
        return DateUtils.parseDate(s, DateUtils.DATE_WEB_FORMAT);
    }

    @Override
    public int updateAmount(int tenantId, BigDecimal amount) {
        return baseMapper.updateAmount(tenantId, amount);
    }
}

