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

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

import com.alibaba.fastjson.JSONObject;
import com.huifu.adapay.core.AdapayCore;
import com.huifu.adapay.core.util.AdapaySign;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.LocalDateTime;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
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 com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ym.mec.biz.dal.dao.CloudTeacherOrderDao;
import com.ym.mec.biz.dal.dao.TenantOrderRecordDao;
import com.ym.mec.biz.dal.dto.TenantOrderRecordDto;
import com.ym.mec.biz.dal.entity.CloudTeacherOrder;
import com.ym.mec.biz.dal.entity.PlatformServe;
import com.ym.mec.biz.dal.entity.PlatformServeDetail;
import com.ym.mec.biz.dal.entity.TenantAssetsInfo;
import com.ym.mec.biz.dal.entity.TenantConfig;
import com.ym.mec.biz.dal.entity.TenantInfo;
import com.ym.mec.biz.dal.entity.TenantOrderRecord;
import com.ym.mec.biz.dal.entity.TenantProductInfo;
import com.ym.mec.biz.dal.entity.TenantProxyDividend;
import com.ym.mec.biz.dal.entity.TenantProxyUserRelation;
import com.ym.mec.biz.dal.enums.DealStatusEnum;
import com.ym.mec.biz.dal.enums.MessageTypeEnum;
import com.ym.mec.biz.dal.enums.PaymentChannelEnum;
import com.ym.mec.biz.dal.enums.PeriodEnum;
import com.ym.mec.biz.dal.enums.TenantOrderRecordEnum;
import com.ym.mec.biz.service.CloudTeacherOrderService;
import com.ym.mec.biz.service.HfMerchantConfigService;
import com.ym.mec.biz.service.PlatformServeDetailService;
import com.ym.mec.biz.service.PlatformServeService;
import com.ym.mec.biz.service.SysMessageService;
import com.ym.mec.biz.service.TenantAssetsInfoService;
import com.ym.mec.biz.service.TenantConfigService;
import com.ym.mec.biz.service.TenantInfoService;
import com.ym.mec.biz.service.TenantOrderRecordService;
import com.ym.mec.biz.service.TenantProductInfoService;
import com.ym.mec.biz.service.TenantProxyDividendService;
import com.ym.mec.biz.service.TenantProxyUserRelationService;
import com.ym.mec.common.exception.BizException;
import com.ym.mec.common.page.PageInfo;
import com.ym.mec.common.page.PageUtil;
import com.ym.mec.common.page.WrapperUtil;
import com.ym.mec.common.tenant.TenantContextHolder;
import com.ym.mec.thirdparty.adapay.Payment;
import com.ym.mec.thirdparty.adapay.entity.HfMerchantConfig;
import com.ym.mec.thirdparty.message.MessageSenderPluginContext;

import javax.servlet.http.HttpServletRequest;

/**
 * 机构付款记录表(TenantOrderRecord)表服务实现类
 *
 * @author hgw
 * @since 2021-12-20 10:08:28
 */
@Service("tenantOrderRecordService")
public class TenantOrderRecordServiceImpl extends ServiceImpl<TenantOrderRecordDao, TenantOrderRecord> implements TenantOrderRecordService {
    private static final Logger log = LoggerFactory.getLogger(TenantOrderRecordServiceImpl.class);

    @Autowired
    private TenantProductInfoService tenantProductInfoService;
    @Autowired
    private PlatformServeService platformServeService;
    @Autowired
    private PlatformServeDetailService platformServeDetailService;
    @Autowired
    private TenantInfoService tenantInfoService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private TenantAssetsInfoService assetsInfoService;
    @Autowired
    private TenantConfigService tenantConfigService;
    @Autowired
    private SysMessageService sysMessageService;
    @Autowired
    private CloudTeacherOrderDao cloudTeacherOrderDao;
    @Autowired
    private CloudTeacherOrderService cloudTeacherOrderService;
    @Autowired
    private TenantProxyDividendService tenantProxyDividendService;
    @Autowired
    private TenantProxyUserRelationService tenantProxyUserRelationService;

    @Autowired
    private HfMerchantConfigService hfMerchantConfigService;

    //订单不存在
    private static final String PAYMENT_ID_NOT_EXISTS = "payment_id_not_exists";

    private static final Map<String, Consumer<TenantOrderRecord>> checkOrder = new HashMap<>();

    {
        //开通缴费
        checkOrder.put(TenantOrderRecordEnum.TENANT_OPEN.getCode(), this::open);
        //续费
        checkOrder.put(TenantOrderRecordEnum.TENANT_RENEW.getCode(), this::renew);
        //激活团练宝
        checkOrder.put(TenantOrderRecordEnum.CLOUD_TEACHER.getCode(), this::activeCloudTeacher);
        //充值
        checkOrder.put(TenantOrderRecordEnum.TENANT_RECHARGE.getCode(), this::rechargeSuccess);
    }

    @Override
    public PageInfo<TenantOrderRecord> queryPage(TenantOrderRecordDto dto) {
        Integer tenantId = TenantContextHolder.getTenantId();
        if (dto.getTenantId() == null) {
            dto.setTenantId(tenantId.toString());
        }
        Page<TenantOrderRecord> pageInfo = PageUtil.getPage(dto.getPage(), dto.getRows());
        pageInfo.setDesc("a.created_time_ ");
        WrapperUtil.toListOptional(dto.getTenantId())
                .ifPresent(dto::setTenantIdList);
        return PageUtil.pageInfo(baseMapper.queryPage(pageInfo, dto));
    }

    /**
     * 校验订单状态
     *
     * @param orderNo 我方订单编号
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> checkTenantOrder(String orderNo) {
        TenantOrderRecord orderRecord = this.getOne(new WrapperUtil<TenantOrderRecord>()
                .hasEq("order_no_", orderNo).queryWrapper());
        return checkTenantOrder(orderRecord);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> checkTenantOrder(TenantOrderRecord orderRecord) {
        return getTenantOrderState(orderRecord);
    }

    private Map<String, Object> getTenantOrderState(TenantOrderRecord orderRecord) {
        //主动去第三方查询订单状态
        checkTransOrderState(orderRecord);
        //返回
        Map<String, Object> result = new HashMap<>();
        result.put("groupType", orderRecord.getOrderType());
        //订单信息
        Map<String, Object> order = new HashMap<>();
        result.put("order", order);
        order.put("orderNo", orderRecord.getOrderNo());
        order.put("expectAmount", orderRecord.getExpectAmount());
        order.put("type", orderRecord.getOrderType());
        //购买的信息
        Map<String, Object> detail = new HashMap<>();
        result.put("detail", detail);
        //查询该机构的财务公章
        TenantConfig tenantConfig = tenantConfigService.getOne(new QueryWrapper<TenantConfig>()
                .eq("tenant_id_", orderRecord.getTenantId()));
        result.put("financeChops", tenantConfig.getCorporateFinanceChops());

        log.info("checkTenantOrder  getOrderState>>>>> {}", JSON.toJSONString(orderRecord));
        getOrderState(orderRecord, result);
        log.info("checkTenantOrder  result >>>>> {}", result);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void checkTenantOrder() {
        RBucket<Object> bucket = redissonClient.getBucket("tenant_check_order");
        if (!bucket.trySet(1, 3L, TimeUnit.SECONDS)) {
            return;
        }
        log.info("delayCheckTenant  checkTenantOrder>>>>>");
        List<TenantOrderRecord> list = this.list(new WrapperUtil<TenantOrderRecord>().queryWrapper()
                .eq("order_state_", 0));
        if (CollectionUtils.isNotEmpty(list)) {
            list.forEach(record -> {
                if (StringUtils.isBlank(record.getTransNo())) {
                    if (timeOutCheck(record.getCreatedTime())) {
                        record.setOrderState(2);
                    } else {
                        return;
                    }
                } else {
                    checkTransOrderState(record);
                    if (record.getOrderState() == 1) {
                        Consumer<TenantOrderRecord> consumer = checkOrder.get(record.getOrderType());
                        if (Objects.isNull(consumer)) {
                            return;
                        }
                        consumer.accept(record);
                    }
                }
                //修改流水
                LocalDateTime now = LocalDateTime.now();
                record.setPayDate(now.toLocalDate().toDate());
                record.setPayTime(now.toDate());
                baseMapper.updateById(record);
            });
        }
        bucket.delete();
    }

    //开通成功
    @Transactional(rollbackFor = Exception.class)
    private void open(TenantOrderRecord record) {
        tenantInfoService.tenantOpenSuccess(record.getTenantId());
    }

    //续费成功
    @Transactional(rollbackFor = Exception.class)
    private void renew(TenantOrderRecord record) {
        //机构产品信息
        TenantProductInfo productInfo = tenantProductInfoService.getOne(new WrapperUtil<TenantProductInfo>()
                .hasEq("tenant_id_", record.getTenantId()).queryWrapper().eq("using_", 0));
        //获取机构续费时长
        RBucket<Integer> bucket = tenantInfoService.opsRenewInfo(record.getTenantId());
        Integer val = bucket.get();
        //修改产品信息
        tenantInfoService.renewSuccess(val, productInfo, record.getActualAmount());
    }

    /**
     * 激活团练宝成功
     *
     * @param record 机构订单记录
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void activeCloudTeacher(TenantOrderRecord record) {
        log.info("activeCloudTeacher>>> record : {}", JSON.toJSONString(record));
        if (record.getId() == null) {
            throw new BizException("订单未找到");
        }
        Date now = new Date();
        record.setPayDate(now);
        record.setPayTime(now);
        updateById(record);
        List<CloudTeacherOrder> cloudTeacherOrders = cloudTeacherOrderDao.findByPlatformOrderId(record.getId());
        for (CloudTeacherOrder cto : cloudTeacherOrders) {
            //激活团练宝
            cloudTeacherOrderService.activationMember(now, cto);
        }
        //发信息
        cloudTeacherOrderService.sendSms(record);
        log.info("activeCloudTeacher>>> cloudTeacherOrders : {}", JSON.toJSONString(cloudTeacherOrders));
        //消费大于0元则且有代理商的机构则写入分润表
        if (record.getActualAmount().compareTo(BigDecimal.ZERO) > 0) {
            log.info("activeCloudTeacher>>> proxy {}", record.getActualAmount());
            //根据机构id查询对应的推荐人
            TenantInfo tenantInfo = tenantInfoService.getById(record.getTenantId());
            log.info("activeCloudTeacher>>> proxy tenantInfo {}", JSON.toJSONString(tenantInfo));
            if (Objects.isNull(tenantInfo) || Objects.isNull(tenantInfo.getRecommender())) {
                return;
            }
            //根据推荐人查询代理商
            TenantProxyUserRelation proxyUserRelation = tenantProxyUserRelationService.getOne(Wrappers.<TenantProxyUserRelation>lambdaQuery()
                    .eq(TenantProxyUserRelation::getUserId, tenantInfo.getRecommender()));
            log.info("activeCloudTeacher>>> proxy proxyUserRelation {}", JSON.toJSONString(proxyUserRelation));
            if (Objects.isNull(proxyUserRelation)) {
                return;
            }
            //获取总激活月份
            Integer totalMonth = cloudTeacherOrders.stream()
                    .map(this::getTotalMonth)
                    .reduce(0, Integer::sum);
            //写入分润表
            TenantProxyDividend dividend = new TenantProxyDividend();
            dividend.setProxyId(proxyUserRelation.getProxyId());
            dividend.setTenantId(record.getTenantId());
            dividend.setTotalMonth(totalMonth);
            dividend.setActiveTime(now);
            dividend.setOrderId(record.getId());
            tenantProxyDividendService.save(dividend);
            log.info("activeCloudTeacher>>> proxy proxyUserRelation {}", JSON.toJSONString(dividend));
        }
    }

    private Integer getTotalMonth(CloudTeacherOrder order) {
        PeriodEnum type = order.getType();
        if (type.equals(PeriodEnum.MONTH)) {
            return order.getTime();
        } else if (type.equals(PeriodEnum.QUARTERLY)) {
            return order.getTime() * 3;
        } else if (type.equals(PeriodEnum.YEAR_HALF)) {
            return order.getTime() * 6;
        } else if (type.equals(PeriodEnum.YEAR)) {
            return order.getTime() * 12;
        } else {
            return 1;
        }
    }

    //充值成功
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rechargeSuccess(TenantOrderRecord record) {
        // 更新 tenantAssetsInfo
        log.info("checkRechargeCheck  updateAmount>>>>>");
        int ret = assetsInfoService.updateAmount(record.getTenantId(), record.getExpectAmount());
        if (ret != 1) {
            throw new BizException("更新支付失败");
        }
        record.setPayDate(new Date());
        record.setPayTime(new Date());
        baseMapper.updateById(record); // 添加支付时间
        // 取余额
        TenantAssetsInfo assets = assetsInfoService.getOne(new WrapperUtil<TenantAssetsInfo>()
                .hasEq("tenant_id_", record.getTenantId()).queryWrapper());
        TenantInfo tenantInfo = tenantInfoService.getOne(new WrapperUtil<TenantInfo>()
                .hasEq("id_", record.getTenantId()).queryWrapper());
        Integer receiveUserId = tenantInfo.getUserId();
        String email = tenantInfo.getEmail();

        Object[] objs = {tenantInfo.getName(), record.getActualAmount(), assets.getBalance()};

        // 发邮件， 发短信
        Map<Integer, String> sendPar = new HashMap<>();
        sendPar.put(receiveUserId, email);
        log.info("platformSendToAll 充值成功发送邮件  >>> receiveUserId {} email {} objs {} sendPar {}", receiveUserId, email, objs, sendPar);
        sysMessageService.batchSendMessage(-1, MessageSenderPluginContext.MessageSender.AWSMS, MessageTypeEnum.SMS_TENANT_RECHARGE, sendPar, null, 0, null, "SYSTEM", objs);

        Map<Integer, String> sendPar2 = new HashMap<>();
        sendPar2.put(receiveUserId, email);
        log.info("platformSendToAll 充值成功发送邮件分发器  >>> receiveUserId {} email {} objs {} sendPar {}", receiveUserId, email, objs, sendPar2);
        sysMessageService.batchSendMessage(-1, MessageSenderPluginContext.MessageSender.EMAIL, MessageTypeEnum.EMAIL_TENANT_RECHARGE, sendPar2, null, 0, null, "SYSTEM", objs);
    }

    //主动去第三方查询订单状态
    private void checkTransOrderState(TenantOrderRecord orderRecord) {
        if (StringUtils.equals(orderRecord.getPaymentChannel(), PaymentChannelEnum.ADAPAY.getCode())) {
            Map<String, Object> payment;
            try {
                Integer tenantId = orderRecord.getTenantId();
                HfMerchantConfig hfMerchantConfig = hfMerchantConfigService.queryByTenantId(tenantId);
                if (hfMerchantConfig == null) {
                    throw new BizException("机构[{}]汇付商户信息找不到", tenantId);
                }

                payment = Payment.queryPayment(orderRecord.getTransNo(), hfMerchantConfig.getMerKey());
                log.info("    >>>>> checkTenantOrder  payment start");
                String status = (String) payment.get("status");
                //判断状态是成功还是失败
                if (status.equals("succeeded")) {
                    orderRecord.setOrderState(1);
                } else if (Objects.nonNull(payment.get("error_code"))) {
                    //若状态是失败则判断code
                    if (!PAYMENT_ID_NOT_EXISTS.equals(payment.get("error_code"))) {
                        //不等于这个异常都是失败
                        orderRecord.setOrderState(2);
                    } else {
                        if (timeOutCheck(orderRecord.getCreatedTime())) {
                            orderRecord.setOrderState(2);
                        }
                    }
                }
                log.info("    >>>>> checkTenantOrder orderType {} payment >>>>> {}", orderRecord.getOrderType(), payment);
                log.info("    >>>>> checkTenantOrder  orderRecord >>>>> {}", JSON.toJSONString(orderRecord));
            } catch (Exception e) {
                log.error("    >>>>> checkTenantOrder  payment error>>>>> {}", JSON.toJSONString(orderRecord));
            }
        }
    }

    //超时返回true
    private Boolean timeOutCheck(Date orderDate) {
        long orderTime = orderDate.getTime();//订单创建时间
        long nowTime = new Date().getTime();
        //丢弃5分钟之前的订单 300000 = 5 * 60秒 * 1000
        long exTime = 300000L;
        return nowTime - orderTime > exTime;
    }

    private void getOrderState(TenantOrderRecord orderRecord, Map<String, Object> result) {
        Map<String, Object> detail = (Map<String, Object>) result.get("detail");
        Map<String, Object> order = (Map<String, Object>) result.get("order");
        String code;
        //订单状态/0待支付、1已支付、2支付失败
        switch (orderRecord.getOrderState()) {
            case 1:
                tenantOrderSuccess(orderRecord, detail);
                Consumer<TenantOrderRecord> consumer = checkOrder.get(orderRecord.getOrderType());
                if (Objects.isNull(consumer)) {
                    code = DealStatusEnum.ING.getCode();
                    break;
                }
                consumer.accept(orderRecord);
                code = DealStatusEnum.SUCCESS.getCode();
                LocalDateTime now = LocalDateTime.now();
                orderRecord.setPayDate(now.toLocalDate().toDate());
                orderRecord.setPayTime(now.toDate());
                //修改流水
                baseMapper.updateById(orderRecord);
                break;
            case 2:
                code = DealStatusEnum.FAILED.getCode();
                //修改流水
                baseMapper.updateById(orderRecord);
                break;
            default:
                code = DealStatusEnum.ING.getCode();
        }
        order.put("status", code);
    }

    private void tenantOrderSuccess(TenantOrderRecord orderRecord, Map<String, Object> detail) {
        //机构产品信息
        TenantProductInfo productInfo = tenantProductInfoService.getOne(new WrapperUtil<TenantProductInfo>()
                .hasEq("tenant_id_", orderRecord.getTenantId()).queryWrapper().eq("using_", 0));
        //获取服务信息
        PlatformServe platformServe = platformServeService.getOne(new WrapperUtil<PlatformServe>()
                .hasEq("id_", productInfo.getServeId()).queryWrapper());
        //获取服务详情
        PlatformServeDetail serveDetail = platformServeDetailService.getOne(new WrapperUtil<PlatformServeDetail>()
                .hasEq("id_", productInfo.getServeDetailId()).queryWrapper());
        detail.put("platformServeName", platformServe.getName());
        detail.put("mode", serveDetail.getMode());
        detail.put("num", productInfo.getExpiryCount());
    }

    /**
     * 机构相关支付-回调
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String callback(HttpServletRequest request) {
        // 验签请参data
        String data = request.getParameter("data");
        // 验签请参sign
        String sign = request.getParameter("sign");
        log.info("tenant callback request param:{}", data);
        try {
            // 验签
            if (AdapaySign.verifySign(data, sign, AdapayCore.PUBLIC_KEY)) {
                JSONObject dataObj = JSON.parseObject(data);
                String transType = request.getParameter("type");
                String orderNo = dataObj.getString("order_no");
                TenantOrderRecord record = this.getOne(Wrappers.<TenantOrderRecord>lambdaQuery()
                        .eq(TenantOrderRecord::getOrderNo, orderNo));
                if(Objects.isNull(record)){
                    log.error("error tenant callback request orderNo is null param:{}", data);
                    return "failed";
                }
                //只处理未支付状态的订单
                if(!record.getOrderState().equals(0)){
                    return "succeeded";
                }
                switch (transType) {
                    case "payment.succeeded":// 支付成功
                        Consumer<TenantOrderRecord> consumer = checkOrder.get(record.getOrderType());
                        if (Objects.nonNull(consumer)) {
                            consumer.accept(record);
                        }
                        LocalDateTime now = LocalDateTime.now();
                        record.setPayDate(now.toLocalDate().toDate());
                        record.setPayTime(now.toDate());
                        //修改流水
                        record.setOrderState(1);
                        baseMapper.updateById(record);
                        break;
                    case "payment.failed":// 支付失败
                        //修改流水
                        record.setOrderState(2);
                        baseMapper.updateById(record);
                        break;
                    default:
                        break;
                }
            } else {
                return "验签失败";
            }
        } catch (Exception e) {
            log.error("error tenant callback request param:{}", data);
            return e.getMessage();
        }
        return "succeeded";
    }

}

