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

import com.alibaba.fastjson.JSON;
import com.ym.mec.biz.dal.dao.StudentPaymentOrderDao;
import com.ym.mec.biz.dal.dao.StudentPaymentRouteOrderDao;
import com.ym.mec.biz.dal.dao.SysConfigDao;
import com.ym.mec.biz.dal.dao.SysUserCashAccountDao;
import com.ym.mec.biz.dal.dto.PageInfoOrder;
import com.ym.mec.biz.dal.dto.SporadicChargeInfoDto;
import com.ym.mec.biz.dal.dto.StudentPaymentOrderExportDto;
import com.ym.mec.biz.dal.entity.Goods;
import com.ym.mec.biz.dal.entity.StudentPaymentOrder;
import com.ym.mec.biz.dal.entity.StudentPaymentRouteOrder;
import com.ym.mec.biz.dal.enums.*;
import com.ym.mec.biz.dal.page.SporadicOrderQueryInfo;
import com.ym.mec.biz.dal.page.StudentPaymentOrderQueryInfo;
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.thirdparty.adapay.ConfigInit;
import com.ym.mec.thirdparty.adapay.Payment;
import com.ym.mec.thirdparty.yqpay.Msg;
import com.ym.mec.thirdparty.yqpay.RsqMsg;
import com.ym.mec.thirdparty.yqpay.YqPayFeignService;
import com.ym.mec.thirdparty.yqpay.YqPayUtil;
import com.ym.mec.util.collection.MapUtil;

import com.ym.mec.util.date.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.snaker.engine.entity.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
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 StudentPaymentOrderServiceImpl extends BaseServiceImpl<Long, StudentPaymentOrder> implements StudentPaymentOrderService {

    @Autowired
    private StudentPaymentOrderDao studentPaymentOrderDao;
    @Autowired
    private YqPayFeignService yqPayFeignService;
    @Autowired
    private StudentRegistrationService studentRegistrationService;
    @Autowired
    @Lazy
    private VipGroupService vipGroupService;
    @Autowired
    private MusicGroupService musicGroupService;
    @Autowired
    private SporadicChargeInfoService sporadicChargeInfoService;
    @Autowired
    private PracticeGroupService practiceGroupService;
    @Autowired
    private SysUserCashAccountDao sysUserCashAccountDao;
    @Autowired
    private StudentRepairService studentRepairService;
    @Autowired
    private SysUserCashAccountService sysUserCashAccountService;
    @Autowired
    private SubjectChangeService subjectChangeService;
    @Autowired
    private DegreeRegistrationService degreeRegistrationService;
    @Autowired
    private StudentPaymentRouteOrderDao studentPaymentRouteOrderDao;
    @Autowired
    private IdGeneratorService idGeneratorService;
    @Autowired
    private SysConfigDao sysConfigDao;

    @Override
    public BaseDAO<Long, StudentPaymentOrder> getDAO() {
        return studentPaymentOrderDao;
    }

    @Override
    public List<Goods> queryApplyGoodsList(String musicGroupId, OrderDetailTypeEnum type) {
        return studentPaymentOrderDao.queryApplyGoodsList(musicGroupId, type);
    }

    @Override
    public List<StudentPaymentOrder> queryByCondition(GroupType groupType, String musicGroupId, Integer userId, DealStatusEnum status, OrderTypeEnum type) {
        return studentPaymentOrderDao.queryByCondition(groupType, musicGroupId, userId, status, type);
    }

    @Override
    public StudentPaymentOrder findMusicGroupApplyOrderByStatus(Integer userId, String musicGroupId, DealStatusEnum status) {
        return studentPaymentOrderDao.findMusicGroupApplyOrderByStatus(userId, musicGroupId, status);
    }

    @Override
    public StudentPaymentOrder findOrderByOrderNo(String orderNo) {
        return studentPaymentOrderDao.findOrderByOrderNo(orderNo);
    }

    @Override
    public int findPayOrderNum() {
        return studentPaymentOrderDao.findPayOrderNum();
    }

    @Override
    public List<StudentPaymentOrder> findOrdersByStatus(DealStatusEnum status, String paymentChannel) {
        return studentPaymentOrderDao.findOrdersByStatus(status, paymentChannel);
    }

    @Override
    public List<StudentPaymentOrder> findOrdersOverTime(List<String> orderNoList, DealStatusEnum status, Date beforeTime) {
        return studentPaymentOrderDao.findOrdersOverTime(orderNoList, status, beforeTime);
    }

    @Override
    public void queryOrderStatus() throws Exception {
        yqPayQuery(null);
        adaPayQuery(null);
        reConfirmOrder(null, null);
    }

    private void yqPayQuery(List<StudentPaymentOrder> payingOrders) throws Exception {
        if (payingOrders == null) {
            payingOrders = findOrdersByStatus(DealStatusEnum.ING, "YQPAY");
        }
        if (payingOrders.size() == 0) {
            return;
        }
        List<String> orderNoList = payingOrders.stream().map(StudentPaymentOrder::getOrderNo).collect(Collectors.toList());
        String merOrderNos = payingOrders.stream().map(StudentPaymentOrder::getOrderNo).collect(Collectors.joining(","));

        String notifyUrl = ""; //回调地址
        Map<String, Object> resultMap = new LinkedHashMap<>();
        resultMap.put("merOrderNoList", merOrderNos);
        Map<String, Object> requestMap = YqPayUtil.getRequestMap(notifyUrl, resultMap);

        RsqMsg rsqMsg = new RsqMsg(requestMap);

        Msg queryRs = yqPayFeignService.orderQuery(rsqMsg);

        if (queryRs.getCode().equals("88")) {
            //更新订单状态
            String[] statusArr = {"0", "1", "7"};
            String responseParameters = queryRs.getResponseParameters();
            List<Map<String, String>> responseList = JSON.parseObject(responseParameters, List.class);
            for (Map<String, String> response : responseList) {
                Map<String, String> rpMap = response;
                String channelType = rpMap.get("channelType").equals("1") ? "WXPay" : (rpMap.get("channelType").equals("2") ? "Alipay" : "quickPay");
                rpMap.put("channelType", channelType);

                if (orderNoList.contains(rpMap.get("merOrderNo"))) {
                    orderNoList.remove(rpMap.get("merOrderNo"));
                }

                if (Arrays.asList(statusArr).contains(rpMap.get("tradeState"))) {
                    try {
                        updateOrder(rpMap); //更新订单
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    continue;
                }
            }
            closeOrders(orderNoList); //关闭订单
        }
    }

    private void adaPayQuery(List<StudentPaymentOrder> payingOrders) throws Exception {
        if (payingOrders == null) {
            payingOrders = findOrdersByStatus(DealStatusEnum.ING, "ADAPAY");
        }
        if (payingOrders.size() == 0) {
            return;
        }
        List<String> orderNoList = new ArrayList<String>();

        for (StudentPaymentOrder payingOrder : payingOrders) {
            if (payingOrder.getTransNo() == null) {
                orderNoList.add(payingOrder.getOrderNo());
                continue;
            }
            Map<String, Object> payment = Payment.queryPayment(payingOrder.getTransNo());
            Map<String, String> rpMap = new HashMap<>();
            rpMap.put("merOrderNo", payingOrder.getOrderNo());
            rpMap.put("orderNo", payingOrder.getTransNo());
            rpMap.put("channelType", (String) payment.get("pay_channel"));
            String status = (String) payment.get("status");
            if (payment.containsKey("error_msg")) {
                rpMap.put("remarks", (String) payment.get("error_msg"));
            }
            if (status.equals("succeeded")) {
                rpMap.put("tradeState", "1");
            }
            if (status.equals("failed")) {
                rpMap.put("tradeState", "0");
            }

            if (status.equals("succeeded") ||
                    (status.equals("failed") && payingOrder.getCreateTime().before(DateUtil.addMinutes(new Date(), -35)))) {
                try {
                    updateOrder(rpMap); //更新订单
                } catch (Exception e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }
        closeOrders(orderNoList);
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateOrder(Map<String, String> rpMap) throws Exception {
        DealStatusEnum status = rpMap.get("tradeState").equals("1") ? DealStatusEnum.SUCCESS : DealStatusEnum.FAILED;
        StudentPaymentOrder order = findOrderByOrderNo(rpMap.get("merOrderNo"));
        //关闭或失败的订单查询订单成功,订单改成成功，钱退到余额
        if (order != null && (order.getStatus().equals(DealStatusEnum.CLOSE) || order.getStatus().equals(DealStatusEnum.FAILED)) && status.equals(DealStatusEnum.SUCCESS)) {
            String memo = order.getStatus().equals(DealStatusEnum.CLOSE) ? "关闭订单" : "失败订单";
            memo = memo + ",实际支付成功,退到用户余额";
            //更新订单状态
            order.setStatus(status);
            order.setTransNo(rpMap.get("orderNo"));
            order.setPaymentBusinessChannel(rpMap.get("channelType"));
            order.setPayTime(new Date());
            order.setMemo(memo);
            int updateCount = this.update(order);
            if (updateCount <= 0) {
                throw new BizException("订单更新失败");
            }
            //增加用户余额
            sysUserCashAccountService.updateBalance(order.getUserId(), order.getActualAmount(),
                    PlatformCashAccountDetailTypeEnum.REFUNDS, memo + ",订单号:" + order.getOrderNo());

            if (StringUtils.isNotBlank(order.getPaymentChannel())
                    && order.getPaymentChannel().equals("ADAPAY")
                    && !rpMap.containsKey("simulation")) {
                confirmOrder(order);
            }
            return;
        }

        if (order == null || !order.getStatus().equals(DealStatusEnum.ING)) {
            return;
        }

        if (status.equals(DealStatusEnum.SUCCESS)) {
            order.setPayTime(new Date());
        } else {
            order.setMemo(rpMap.get("remarks"));
        }
        order.setStatus(status);
        if (StringUtils.isBlank(order.getTransNo())) {
            order.setTransNo(rpMap.get("orderNo"));
        }
        order.setPaymentBusinessChannel(rpMap.get("channelType"));

        callOrderCallBack(order);

        if (status.equals(DealStatusEnum.SUCCESS)
                && StringUtils.isNotBlank(order.getPaymentChannel())
                && order.getPaymentChannel().equals("ADAPAY")
                && !rpMap.containsKey("simulation")) {
            confirmOrder(order);
        }
    }

    private Boolean confirmOrder(StudentPaymentOrder order) {
        List<StudentPaymentRouteOrder> routeOrders = studentPaymentRouteOrderDao.getRouteOrders(order.getOrderNo());
        for (StudentPaymentRouteOrder routeOrder : routeOrders) {
            Map<String, Object> divMember = new HashMap<>();
            divMember.put("member_id", routeOrder.getMerNo());//分佣账户
            divMember.put("amount", routeOrder.getRouteAmount().setScale(2, BigDecimal.ROUND_HALF_UP));//分佣金额
            divMember.put("fee_flag", "Y");
            if (routeOrder.getMerNo().equals(ConfigInit.merNo)) {
                divMember.put("member_id", 0);
            }
            List<Map<String, Object>> divMembers = new ArrayList<>();
            divMembers.add(divMember);

            Map<String, Object> confirm = new HashMap<>();
            confirm.put("payment_id", order.getTransNo());
            confirm.put("order_no", idGeneratorService.generatorId("payment"));
            confirm.put("confirm_amt", routeOrder.getRouteAmount());
            confirm.put("div_members", divMembers);
            try {
                Payment.createConfirm(confirm);
            } catch (Exception e) {
                continue;
            }
        }
        return true;
    }

    //Date date = new Date();
    //        BigDecimal balance = BigDecimal.ZERO;
    //        MusicGroup musicGroup = musicGroupDao.get(studentRegistration.getMusicGroupId());
    //        if (registerPayDto.getIsUseBalancePayment() && amount.compareTo(BigDecimal.ZERO) > 0) {
    //            SysUserCashAccount userCashAccount = sysUserCashAccountService.getLocked(userId);
    //            if (userCashAccount == null) {
    //                throw new BizException("用户账户找不到");
    //            }
    //            if (userCashAccount.getBalance() != null && userCashAccount.getBalance().compareTo(BigDecimal.ZERO) > 0) {
    //                balance = amount.compareTo(userCashAccount.getBalance()) >= 0 ? userCashAccount.getBalance() : amount;
    //                amount = amount.subtract(balance);
    //                studentPaymentOrder.setPaymentChannel("BALANCE");
    //                studentPaymentOrder.setActualAmount(amount);
    //                studentPaymentOrder.setBalancePaymentAmount(balance);
    //                sysUserCashAccountService.updateBalance(userId, balance.negate(), PlatformCashAccountDetailTypeEnum.PAY_FEE, "乐团报名");
    //            }
    //        }
    //        studentPaymentOrder.setPaymentChannel("BALANCE");
    //        studentPaymentOrder.setRemitFee(remitFee);
    //        studentPaymentOrder.setCourseRemitFee(courseRemitFee);
    //        studentPaymentOrder.setOrganId(musicGroup.getOrganId());
    //        studentPaymentOrder.setRoutingOrganId(musicGroup.getOrganId());
    //        studentPaymentOrder.setUpdateTime(date);
    //        studentPaymentOrderService.update(studentPaymentOrder);
    //        studentPaymentOrder.setVersion(studentPaymentOrder.getVersion() + 1);
    //
    //        if (amount.compareTo(BigDecimal.ZERO) == 0) {
    //            //如果赠送网管课
    //            studentPaymentRouteOrderService.addRouteOrder(orderNo, musicGroup.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;
    //        }

    //调用相应业务回调接口
    private void callOrderCallBack(StudentPaymentOrder order) throws Exception {
        if (order.getType().equals(OrderTypeEnum.APPLY)) { //报名订单
            studentRegistrationService.updateApplyOrder(order);
        } else if (order.getType().equals(OrderTypeEnum.SMALL_CLASS_TO_BUY)) {
            vipGroupService.orderCallback(order);
        } else if (order.getType().equals(OrderTypeEnum.RENEW)) {
            musicGroupService.renewForCallback(order);
        } else if (order.getType().equals(OrderTypeEnum.SPORADIC) || order.getType().equals(OrderTypeEnum.LUCK) || order.getType().equals(OrderTypeEnum.DOUBLE_ELEVEN2020)) {
            sporadicChargeInfoService.renewForCallback(order);
        } else if (order.getType().equals(OrderTypeEnum.PRACTICE_GROUP_BUY) || order.getType().equals(OrderTypeEnum.PRACTICE_GROUP_RENEW)) {
            practiceGroupService.orderCallback(order);
        } else if (order.getType().equals(OrderTypeEnum.REPAIR)) {
            studentRepairService.orderCallback(order);
        } else if (order.getType().equals(OrderTypeEnum.GOODS_SELL)) {
            studentRepairService.goodsSellOrderCallback(order);
        } else if (order.getType().equals(OrderTypeEnum.SUBJECT_CHANGE)) {
            subjectChangeService.orderCallback(order);
        } else if (order.getType().equals(OrderTypeEnum.DEGREE_REGISTRATION)) {
            degreeRegistrationService.updateStatus(order);
        }
    }

    @Override
    public Integer findOrderByGroupType(Integer userId, Integer sporadicId, String sporadic, DealStatusEnum status) {
        return studentPaymentOrderDao.findOrderByGroupType(userId, sporadicId, sporadic, status);
    }


    private void closeOrders(List<String> orderNoList) throws Exception {
        if (orderNoList.size() == 0) {
            return;
        }

        Calendar beforeTime = Calendar.getInstance();
        beforeTime.add(Calendar.MINUTE, -35);// 30分钟之前的时间
        Date beforeDate = beforeTime.getTime();

        List<StudentPaymentOrder> ordersOverTime = findOrdersOverTime(orderNoList, DealStatusEnum.ING, beforeDate);
        for (StudentPaymentOrder order : ordersOverTime) {
            try {
                order.setStatus(DealStatusEnum.FAILED);
                order.setMemo("超时未支付关闭");
                callOrderCallBack(order);
            } catch (Exception e) {
                e.printStackTrace();
                continue;
            }
        }

    }

    @Override
    public List<StudentPaymentOrderExportDto> ExportQueryPage(Map<String, Object> params) {
        return studentPaymentOrderDao.ExportQueryPage(params);
    }

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

        List<SporadicChargeInfoDto> dataList = null;
        int count = studentPaymentOrderDao.countSporadicPage(params);
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            dataList = studentPaymentOrderDao.sporadicQueryPage(params);
        }
        if (count == 0) {
            dataList = new ArrayList<>();
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }

    @Override
    public StudentPaymentOrder getOrderMoneyAmount(StudentPaymentOrderQueryInfo queryInfo) {
        Map<String, Object> params = new HashMap<String, Object>();
        MapUtil.populateMap(params, queryInfo);
        return studentPaymentOrderDao.getOrderMoneyAmount(params);
    }

    @Override
    public PageInfoOrder<StudentPaymentOrder> OrderQueryPage(StudentPaymentOrderQueryInfo queryInfo) {
        PageInfoOrder<StudentPaymentOrder> pageInfo = new PageInfoOrder<>(queryInfo.getPage(), queryInfo.getRows());
        Map<String, Object> params = new HashMap<String, Object>();
        MapUtil.populateMap(params, queryInfo);

        List<StudentPaymentOrder> dataList = new ArrayList<>();
        int count = this.findCount(params);

        BigDecimal totalUserBalance = sysUserCashAccountDao.getTotalUserBalance(params);
        pageInfo.setTotalUserBalance(totalUserBalance);
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            dataList = studentPaymentOrderDao.queryPageOrder(params);

            StudentPaymentOrder orderMoneyAmount = studentPaymentOrderDao.getOrderMoneyAmount(params);
            BigDecimal totalExpectAmount = orderMoneyAmount.getExpectAmount() != null ? orderMoneyAmount.getExpectAmount() : BigDecimal.ZERO;
            BigDecimal totalActualAmount = orderMoneyAmount.getActualAmount() != null ? orderMoneyAmount.getActualAmount() : BigDecimal.ZERO;
            BigDecimal totalBalancePaymentAmount = orderMoneyAmount.getBalancePaymentAmount() != null ? orderMoneyAmount.getBalancePaymentAmount() : BigDecimal.ZERO;

            params.put("chargeType", SporadicChargeTypeEnum.RECHARGE);
            BigDecimal totalChargeAmount = studentPaymentOrderDao.getChargeAmount(params);
            totalChargeAmount = totalChargeAmount != null ? totalChargeAmount : BigDecimal.ZERO;
            BigDecimal totalAdvanceAmount = totalChargeAmount.subtract(totalBalancePaymentAmount);

            pageInfo.setTotalAdvanceAmount(totalAdvanceAmount);
            pageInfo.setTotalExpectAmount(totalExpectAmount);
            pageInfo.setTotalActualAmount(totalActualAmount);
            pageInfo.setTotalRevenueAmount(totalActualAmount.subtract(totalAdvanceAmount));
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }

    @Override
    public Boolean getOrderStatus(Long id) throws Exception {
        StudentPaymentOrder studentPaymentOrder = studentPaymentOrderDao.get(id);
        List<StudentPaymentOrder> orderList = new ArrayList<>();
        orderList.add(studentPaymentOrder);
        if (studentPaymentOrder.getPaymentChannel().equals("ADAPAY")) {
            adaPayQuery(orderList);
        } else if (studentPaymentOrder.getPaymentChannel().equals("YQPAY")) {
            yqPayQuery(orderList);
        }
        return true;
    }

    @Override
    public Integer findOrganPayOrderNum(Integer organId) {
        return studentPaymentOrderDao.findOrganPayOrderNum(organId);
    }

    @Override
    public List<StudentPaymentOrder> getUserOrderByType(Integer userId, OrderTypeEnum type, DealStatusEnum status) {
        return studentPaymentOrderDao.getUserOrderByType(userId, type, status);
    }

    @Override
    public List<StudentPaymentOrder> reConfirmOrder(Date startTime, Date endTime) {
        if (startTime == null) {
            endTime = new Date();
            startTime = DateUtil.addMinutes(endTime, -20);
        }

        List<StudentPaymentOrder> orders = studentPaymentOrderDao.getAdaPayOrdersByTime(startTime, endTime);
        String tips = "";
        for (StudentPaymentOrder order : orders) {
            try {
                this.confirmOrder(order);
            } catch (Exception e) {
                tips += order.getOrderNo() + ":" + e.getMessage() + ",";
                continue;
            }
        }
        if (StringUtils.isNotBlank(tips)) {
            throw new BizException(tips);
        }
        return orders;
    }
}
