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

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.ym.mec.biz.dal.dao.*;
import com.ym.mec.biz.dal.entity.*;
import com.ym.mec.thirdparty.adapay.ConfigInit;
import com.ym.mec.thirdparty.adapay.Payment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.ym.mec.biz.dal.dto.PageInfoOrder;
import com.ym.mec.biz.dal.entity.SysTenantAccountDetail.TransType;
import com.ym.mec.biz.dal.entity.TenantEntryActivities.SuitableUser;
import com.ym.mec.biz.dal.entity.TenantPaymentOrder.TenantPaymentType;
import com.ym.mec.biz.dal.enums.DealStatusEnum;
import com.ym.mec.biz.dal.enums.PaymentChannelEnum;
import com.ym.mec.biz.dal.page.TenantPaymentOrderQueryInfo;
import com.ym.mec.biz.service.PayService;
import com.ym.mec.biz.service.SysTenantAccountService;
import com.ym.mec.biz.service.TenantPaymentOrderService;
import com.ym.mec.common.dal.BaseDAO;
import com.ym.mec.common.entity.HttpResponseResult;
import com.ym.mec.common.exception.BizException;
import com.ym.mec.common.service.IdGeneratorService;
import com.ym.mec.common.service.impl.BaseServiceImpl;
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;

@Service
public class TenantPaymentOrderServiceImpl extends BaseServiceImpl<Long, TenantPaymentOrder> implements TenantPaymentOrderService {

    private static final Logger logger = LoggerFactory.getLogger(TenantPaymentOrderServiceImpl.class);

    @Autowired
    private TenantPaymentOrderDao tenantPaymentOrderDao;

    @Autowired
    private TenantEntryActivitiesDao tenantEntryActivitiesDao;

    @Autowired
    private IdGeneratorService idGeneratorService;

    @Autowired
    private PayService payService;

    @Autowired
    private SysConfigDao sysConfigDao;

    @Autowired
    private TeacherDao teacherDao;

    @Autowired
    private SysTenantAccountService sysTenantAccountService;

    @Autowired
    private YqPayFeignService yqPayFeignService;
    @Autowired
    private StudentPaymentRouteOrderDao studentPaymentRouteOrderDao;

    @Override
    public BaseDAO<Long, TenantPaymentOrder> getDAO() {
        return tenantPaymentOrderDao;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public HttpResponseResult createOrder(Integer userId, Integer activitiesId, boolean isContinuePay) {

        Teacher teacher = teacherDao.get(userId);
        if (teacher == null) {
            throw new BizException("用户信息不存在");
        }

        Integer organId = teacher.getTeacherOrganId();

        TenantEntryActivities tenantEntryActivities = tenantEntryActivitiesDao.get(activitiesId);
        if (tenantEntryActivities == null) {
            throw new BizException("活动不存在");
        }

        Date date = new Date();
        // 判断活动是否有效
        if (tenantEntryActivities.getDelFlag() == true) {
            throw new BizException("活动已下架");
        }
        if (date.before(tenantEntryActivities.getStartDate())) {
            throw new BizException("活动未开始");
        }
        if (date.after(tenantEntryActivities.getEndDate())) {
            throw new BizException("活动已结束");
        }

        // 查询用户订单
        List<TenantPaymentOrder> orderList = tenantPaymentOrderDao.queryByUserIdAndActivitiesId(userId, activitiesId);
        if (orderList != null && orderList.size() > 0) {
            for (TenantPaymentOrder po : orderList) {
                // 是否满足条件
                if (tenantEntryActivities.getSuitableUser() == SuitableUser.NEW) {
                    if (po.getStatus() == DealStatusEnum.SUCCESS) {
                        throw new BizException("首充优惠活动每个用户限参与一次。");
                    }

                    if (po.getStatus() == DealStatusEnum.ING) {
                        int i = 35 - DateUtil.minutesBetween(po.getCreateTime(), date);
                        if (i <= 0) {
                            throw new BizException("您有支付中的订单出现异常，请联系客服");
                        }
                        // throw new BizException("您参加的首充活动正在处理中，预计于{}分钟后处理完毕，请稍后。", i);
                        if (isContinuePay == false) {
                            HttpResponseResult result = new HttpResponseResult(true, HttpStatus.CONTINUE, "您存在支付中的订单，是否重新付款？", null);
                            return result;
                        } else {
                            // 关闭原订单
                            po.setStatus(DealStatusEnum.CLOSE);
                            po.setMemo("用户主动关闭");
                            po.setUpdateTime(date);
                            tenantPaymentOrderDao.update(po);
                        }
                    }
                }
            }
        }

        TenantPaymentOrder tenantPaymentOrder = new TenantPaymentOrder();
        tenantPaymentOrder.setUserId(userId);
        tenantPaymentOrder.setTransMinutes(tenantEntryActivities.getPurchaseMinutes());
        tenantPaymentOrder.setGiveMinutes(tenantEntryActivities.getGiveMinutes());
        tenantPaymentOrder.setOrderNo(idGeneratorService.generatorId("payment") + "");
        tenantPaymentOrder.setType(TenantPaymentType.RECHARGE);
        tenantPaymentOrder.setExpectAmount(tenantEntryActivities.getDiscountPrice());
        tenantPaymentOrder.setStatus(DealStatusEnum.ING);
        tenantPaymentOrder.setCreateTime(date);
        tenantPaymentOrder.setUpdateTime(date);
        tenantPaymentOrder.setVersion(0);
        tenantPaymentOrder.setOrganId(organId);
        tenantPaymentOrder.setActivitiesId(activitiesId);
        tenantPaymentOrderDao.insert(tenantPaymentOrder);

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

        // 分类费用 course，instrument，accessories，other
        Map<String, BigDecimal> classFee = new HashMap<>();
        classFee.put("course", BigDecimal.ZERO);
        classFee.put("instrument", BigDecimal.ZERO);
        classFee.put("accessories", BigDecimal.ZERO);
        classFee.put("other", tenantPaymentOrder.getExpectAmount());

        try {

            Map<String, Object> payMap = payService.getPayMap(
                    tenantPaymentOrder.getExpectAmount(),
                    BigDecimal.ZERO,
                    tenantPaymentOrder.getOrderNo(),
                    baseApiUrl + "/api-teacher/teacherOrder/notify",
                    baseApiUrl + "/api-teacher/teacherOrder/paymentResult?orderNo=" + tenantPaymentOrder.getOrderNo(),
                    "商户入驻",
                    "商户充值",
                    organId,
                    "tenantPayment"
            );

            tenantPaymentOrder.setMerNos((String) payMap.get("routingMerNos"));
            tenantPaymentOrder.setPaymentChannel(PaymentChannelEnum.valueOf(payMap.get("type").toString()));
            tenantPaymentOrder.setUpdateTime(date);
            tenantPaymentOrderDao.update(tenantPaymentOrder);

            HttpResponseResult result = new HttpResponseResult(true, HttpStatus.OK, payMap, null);
            return result;
        } catch (Exception e) {
            throw new BizException("调用支付接口出错", e);
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public boolean paymentForCallback(TenantPaymentOrder tenantPaymentOrder) {

        Integer activitiesId = tenantPaymentOrder.getActivitiesId();

        Date date = new Date();
        tenantPaymentOrder.setUpdateTime(date);
        tenantPaymentOrderDao.update(tenantPaymentOrder);

        Integer userId = tenantPaymentOrder.getUserId();

        if (tenantPaymentOrder.getStatus() == DealStatusEnum.SUCCESS) {

            TenantEntryActivities tenantEntryActivities = tenantEntryActivitiesDao.get(activitiesId);
            if (tenantEntryActivities == null) {
                throw new BizException("活动找不到");
            }

            // 更新账户表信息
            sysTenantAccountService.update(userId, tenantEntryActivities.getPurchaseMinutes() + tenantEntryActivities.getGiveMinutes(),
                    tenantPaymentOrder.getOrderNo(), TransType.RECHARGE, "", tenantEntryActivities.getDiscountPrice(), "");

            this.confirmOrder(tenantPaymentOrder);
        } else {

        }

        return true;
    }

    private Boolean confirmOrder(TenantPaymentOrder 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;
    }


    @Override
    public void queryOrderStatusFromRemote() {
        try {
            yqPayQuery();
        } catch (Exception e) {
            throw new BizException("查询订单状态出现异常");
        }
        try {
            adaPayQuery();
        } catch (Exception e) {
            throw new BizException("查询订单状态出现异常");
        }
    }

    private void yqPayQuery() throws Exception {
        List<TenantPaymentOrder> payingOrders = tenantPaymentOrderDao.findOrdersByStatus(DealStatusEnum.ING, "YQPAY");

        if (payingOrders.size() == 0) {
            return;
        }
        List<String> orderNoList = payingOrders.stream().map(TenantPaymentOrder::getOrderNo).collect(Collectors.toList());
        String merOrderNos = payingOrders.stream().map(TenantPaymentOrder::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);
        logger.info("支付状态查询结果：{}", queryRs);
        if (queryRs.getCode().equals("88")) {
            // 更新订单状态
            String[] statusArr = {"0", "1", "7"};
            String responseParameters = queryRs.getResponseParameters();
            List<Map<String, Object>> responseList = JSON.parseObject(responseParameters, List.class);
            for (Map<String, Object> response : responseList) {
                Map<String, Object> 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 {
                        DealStatusEnum status = rpMap.get("tradeState").equals("1") ? DealStatusEnum.SUCCESS : DealStatusEnum.FAILED;
                        TenantPaymentOrder order = tenantPaymentOrderDao.findOrderByOrderNo(rpMap.get("merOrderNo").toString());
                        if (order == null || !order.getStatus().equals(DealStatusEnum.ING)) {
                            continue;
                        }

                        if (status.equals(DealStatusEnum.SUCCESS)) {
                            order.setPayTime(new Date());
                            order.setActualAmount(new BigDecimal((String) rpMap.get("payAmount")));
                        } else {
                            Object remarks = rpMap.get("remarks");
                            if (remarks != null) {
                                order.setMemo(remarks.toString());
                            }
                        }
                        order.setStatus(status);
                        order.setTransNo(rpMap.get("orderNo").toString());
                        order.setPaymentBusinessChannel(channelType);
                        paymentForCallback(order);
                    } catch (Exception e) {
                        e.printStackTrace();
                        continue;
                    }
                }
            }

            Date date = new Date();
            for (TenantPaymentOrder po : payingOrders) {
                if (orderNoList.contains(po.getOrderNo())) {
                    // 超过30分钟的关闭订单
                    if (DateUtil.addMinutes(po.getCreateTime(), 30).before(date)) {
                        po.setStatus(DealStatusEnum.FAILED);
                        po.setMemo("超时未支付关闭");
                        paymentForCallback(po);
                    }
                }
            }
        }
    }

    private void adaPayQuery() throws Exception {
        List<TenantPaymentOrder> payingOrders = tenantPaymentOrderDao.findOrdersByStatus(DealStatusEnum.ING, "ADAPAY");
        if (payingOrders.size() == 0) {
            return;
        }

        List<String> orderNoList = new ArrayList<String>();

        for (TenantPaymentOrder payingOrder : payingOrders) {
            if (payingOrder.getTransNo() == null) {
                orderNoList.add(payingOrder.getOrderNo());
                continue;
            }
            Map<String, Object> payment = Payment.queryPayment(payingOrder.getTransNo());

            String channelType = (String) payment.get("pay_channel");
            String payStatus = (String) payment.get("status");
            String transNo = (String) payment.get("id");
            String memo = null;
            if (payment.containsKey("error_msg")) {
                memo = (String) payment.get("error_msg");
            }
            DealStatusEnum status = null;
            if (payStatus.equals("succeeded")) {
                status = DealStatusEnum.SUCCESS;
                payingOrder.setActualAmount(new BigDecimal((String) payment.get("pay_amt")));
            } else if (payStatus.equals("failed")) {
                status = DealStatusEnum.FAILED;
            }

            if (status != null) {
                payingOrder.setStatus(status);
                payingOrder.setTransNo(transNo);
                payingOrder.setPaymentBusinessChannel(channelType);
                payingOrder.setMemo(memo);
                try {
                    paymentForCallback(payingOrder);
                } catch (Exception e) {
                    e.printStackTrace();
                    continue;
                }
            }

            if (status.equals("pending")) {
                orderNoList.add(payingOrder.getOrderNo());
            }
        }

        Date date = new Date();
        for (TenantPaymentOrder po : payingOrders) {
            if (orderNoList.contains(po.getOrderNo())) {
                // 超过30分钟的关闭订单
                if (DateUtil.addMinutes(po.getCreateTime(), 30).before(date)) {
                    po.setStatus(DealStatusEnum.FAILED);
                    po.setMemo("超时未支付关闭");
                    paymentForCallback(po);
                }
            }
        }
    }

    @Override
    public TenantPaymentOrder queryByOrderNo(String orderNo) {
        return tenantPaymentOrderDao.findOrderByOrderNo(orderNo);
    }

    @Override
    public Object queryTenanPaymentPage(TenantPaymentOrderQueryInfo queryInfo) {
        PageInfoOrder<TenantPaymentOrder> pageInfo = new PageInfoOrder<>(queryInfo.getPage(), queryInfo.getRows());
        Map<String, Object> params = new HashMap<String, Object>();
        MapUtil.populateMap(params, queryInfo);
        List<TenantPaymentOrder> dataList = new ArrayList<>();
        int count = this.findCount(params);
        if (count > 0) {
            TenantPaymentOrder orderMoneyAmount = tenantPaymentOrderDao.getOrderMoneyAmount(params);
            if (orderMoneyAmount != null) {
                pageInfo.setTotalExpectAmount(orderMoneyAmount.getExpectAmount() != null ? orderMoneyAmount.getExpectAmount() : BigDecimal.ZERO);
                pageInfo.setTotalActualAmount(orderMoneyAmount.getActualAmount() != null ? orderMoneyAmount.getActualAmount() : BigDecimal.ZERO);
            }
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            dataList = tenantPaymentOrderDao.queryPage(params);
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }
}