package com.ghy.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ghy.common.adapay.model.AdapayStatusEnum;
import com.ghy.common.adapay.model.DivMember;
import com.ghy.common.adapay.model.PaymentDTO;
import com.ghy.common.constant.UserConstants;
import com.ghy.common.core.text.Convert;
import com.ghy.common.enums.AdapayOrderType;
import com.ghy.common.enums.OrderStatus;
import com.ghy.common.enums.PayStatus;
import com.ghy.common.utils.AdapayUtils;
import com.ghy.order.domain.OrderDetail;
import com.ghy.order.domain.OrderMaster;
import com.ghy.order.mapper.OrderDetailMapper;
import com.ghy.order.mapper.OrderMasterMapper;
import com.ghy.order.service.OrderDetailService;
import com.ghy.order.service.OrderMasterService;
import com.ghy.payment.domain.FinancialDetail;
import com.ghy.payment.domain.FinancialMaster;
import com.ghy.payment.service.AdapayService;
import com.ghy.payment.service.FinancialDetailService;
import com.ghy.payment.service.FinancialMasterService;
import com.huifu.adapay.core.exception.BaseAdaPayException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

import static java.time.temporal.ChronoField.*;

@Service
public class OrderDetailServiceImpl implements OrderDetailService {

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

    private static final AtomicLong INDEX = new AtomicLong(1L);

    public static final DateTimeFormatter MINI_FORMATTER = new DateTimeFormatterBuilder()
            .appendValue(YEAR, 4).appendValue(MONTH_OF_YEAR, 2)
            .appendValue(DAY_OF_MONTH, 2).appendValue(HOUR_OF_DAY, 2)
            .appendValue(MINUTE_OF_HOUR, 2).appendValue(SECOND_OF_MINUTE, 2).toFormatter();

    @Resource
    private ThreadPoolTaskExecutor executor;
    @Resource
    private OrderDetailMapper orderDetailMapper;
    @Resource
    private OrderMasterMapper orderMasterMapper;
    @Resource
    private AdapayService adapayService;
    @Resource
    private OrderMasterService orderMasterService;
    @Resource
    private FinancialMasterService financialMasterService;
    @Resource
    private FinancialDetailService financialDetailService;

    @Override
    public int insertOrderDetail(OrderDetail orderDetail) {
        return orderDetailMapper.insertOrderDetail(orderDetail);
    }

    @Override
    public int updateOrderDetail(OrderDetail orderDetail) {
        if (orderDetail.getOrderStatus() == null) {
            return orderDetailMapper.updateOrderDetail(orderDetail);
        }
        int orderStatus = orderDetail.getOrderStatus();
        boolean flag = Boolean.TRUE;
        int result = orderDetailMapper.updateOrderDetail(orderDetail);
        // 查子单信息
        OrderDetail detailInfo = this.orderDetailMapper.selectById(orderDetail.getId());
        // 判断是否需要更新主单状态.即是否有
        for (OrderDetail detail : orderDetailMapper.selectByOrderMasterId(detailInfo.getOrderMasterId())) {
            if (detail.getOrderStatus() < orderStatus) {
                flag = false;
                break;
            }
        }
        if (flag) {
            OrderMaster orderMaster = orderMasterMapper.selectById(detailInfo.getOrderMasterId());
            if (orderMaster.getOrderStatus() != OrderStatus.PLAIN.code()) {
                orderMaster.setOrderStatus(orderStatus);
                orderMasterMapper.updateOrderMaster(orderMaster);
            }
        }
        return result;
    }

    @Override
    public List<OrderDetail> selectOrderDetailList(OrderDetail orderDetail) {
        return orderDetailMapper.selectOrderDetailList(orderDetail);
    }

    @Override
    public OrderDetail selectById(Long orderDetailId) {
        return orderDetailMapper.selectById(orderDetailId);
    }

    @Override
    public List<OrderDetail> selectByOrderMasterId(Long orderMasterId) {
        return orderDetailMapper.selectByOrderMasterId(orderMasterId);
    }

    @Override
    public int deleteOrderDetailByIds(String ids) {
        Long[] orderDetailIds = Convert.toLongArray(ids);
        return orderDetailMapper.deleteOrderDetailByIds(orderDetailIds);
    }

    @Override
    public String checkOrderDetailCodeUnique(OrderDetail orderDetail) {
        long orderDetailId = orderDetail.getId() == null ? -1L : orderDetail.getId();
        OrderDetail info = orderDetailMapper.checkOrderDetailCodeUnique(orderDetail.getCode());
        if (info != null && info.getId() != orderDetailId) {
            return UserConstants.ORDER_CODE_NOT_UNIQUE;
        }
        return UserConstants.ORDER_CODE_UNIQUE;
    }

    @Override
    public String createCode() {
        INDEX.compareAndSet(9999L, 1L);
        LocalDateTime now = LocalDateTime.now();
        return "od" + now.format(MINI_FORMATTER) + INDEX.getAndIncrement();
    }

    @Override
    public Map<Long, List<OrderDetail>> byOrderIdInMap(List<Long> orderIdList) {
        Map<Long, List<OrderDetail>> longByOrderDetailListMap = new HashMap<>();
        if (orderIdList != null && orderIdList.size() > 0) {
            List<OrderDetail> orderDetailList = orderDetailMapper.getByOrderIdList(orderIdList);
            if (orderDetailList != null && orderDetailList.size() > 0) {
                for (OrderDetail orderDetail : orderDetailList) {
                    List<OrderDetail> orderDetailListByMapCrw = longByOrderDetailListMap.get(orderDetail.getOrderMasterId());
                    if (orderDetailListByMapCrw != null && orderDetailListByMapCrw.size() > 0) {
                        List<OrderDetail> orderDetailListByMapNew = new ArrayList<>();
                        orderDetailListByMapNew.add(orderDetail);
                        longByOrderDetailListMap.put(orderDetail.getOrderMasterId(), orderDetailListByMapNew);
                    } else {
                        List<OrderDetail> orderDetailListByMapNew = new ArrayList<>();
                        orderDetailListByMapNew.add(orderDetail);
                        longByOrderDetailListMap.put(orderDetail.getOrderMasterId(), orderDetailListByMapNew);
                    }
                }
            }
        }
        return longByOrderDetailListMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancel(Long orderDetailId) {
        OrderDetail orderDetail = selectById(orderDetailId);
        Assert.notNull(orderDetail, "找不到对应的子订单");

        // 查询出所有的子订单 如果除了这个子订单以外 没有其它未取消的订单 就走主订单取消流程
        List<OrderDetail> orderDetails = orderDetailMapper.selectByOrderMasterId(orderDetail.getOrderMasterId());
        boolean allCancel = true;
        for (OrderDetail od : orderDetails) {
            if (!od.getId().equals(orderDetailId)) {
                if (!od.getOrderStatus().equals(OrderStatus.CANCEL.code())) {
                    allCancel = false;
                }
            }
        }
        if (allCancel) {
            logger.info("主订单[id={}]下所有的子订单都被取消 直接进入主订单取消流程", orderDetail.getOrderMasterId());
            orderMasterService.cancel(orderDetail.getOrderMasterId());
            return;
        }

        switch (orderDetail.getOrderStatus()) {
            case 0:
            case 1: // 待接单和待排期状态可直接取消
                orderDetailMapper.updateStatus(orderDetailId, OrderStatus.CANCEL.code());
                // 发起退款
                try {
                    refund(orderDetail);
                } catch (BaseAdaPayException e) {
                    logger.error("子订单[code={}]退款失败", orderDetail.getCode(), e);
                }
                break;
            case 2: // 待上门状态需要师傅同意才能取消
                orderDetailMapper.updateStatus(orderDetailId, OrderStatus.GOING_CANCEL.code());
                break;
            case 3: // 服务中状态需要师傅同意才能取消
                orderDetailMapper.updateStatus(orderDetailId, OrderStatus.SERVER_CANCEL.code());
                break;
            case 4: // 待确认状态需要师傅同意才能取消
                orderDetailMapper.updateStatus(orderDetailId, OrderStatus.FINISH_CHECK_CANCEL.code());
                break;
            default: // 其他状态不可取消
                throw new IllegalArgumentException("这个子订单不能取消了");
        }
    }

    private void refund(OrderDetail orderDetail) throws BaseAdaPayException {
        FinancialDetail financialDetail = financialDetailService.selectByOrderDetailId(orderDetail.getId());
        BigDecimal payMoney = financialDetail.getPayMoney();

        // 把子订单对应的子财务单状态更新为已退款
        FinancialDetail update = new FinancialDetail();
        update.setId(financialDetail.getId());
        update.setPayStatus(PayStatus.REFUND.getCode());
        financialDetailService.updateFinancialDetail(update);

        if (BigDecimal.ZERO.compareTo(payMoney) > -1) {
            logger.info("子订单[code={}]待退款金额<=0，不需要退款", orderDetail.getCode());
            return;
        }

        // 发起退款
        OrderMaster orderMaster = orderMasterMapper.selectById(orderDetail.getOrderMasterId());
        FinancialMaster financialMaster = financialMasterService.selectByOrderMasterId(orderMaster.getId());
        String refundAmt = AdapayUtils.bigDecimalToString(payMoney);
        JSONObject response = adapayService.payReverse(orderMaster.getDeptId(), financialMaster.getPaymentId(), refundAmt);
        logger.debug("子订单退款结果: " + response.toJSONString());
        boolean status = AdapayStatusEnum.pending.code.equals(response.getString("status")) ||
                AdapayStatusEnum.succeeded.code.equals(response.getString("status"));
        // 如果退款失败 这里携带失败原因抛出异常 回滚订单状态
        Assert.isTrue(status, response.getString("error_msg"));
        logger.info("子订单[code={}]退款成功", orderDetail.getCode());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelAgree(Long orderDetailId, Integer agree) {
        OrderDetail orderDetail = selectById(orderDetailId);
        Assert.notNull(orderDetail, "找不到对应的子订单");
        if (Integer.valueOf(1).equals(agree)) {
            // 师傅同意取消订单
            // 修改主订单状态为已取消
            orderDetailMapper.updateStatus(orderDetailId, OrderStatus.CANCEL.code());
            // 发起退款
            try {
                refund(orderDetail);
            } catch (BaseAdaPayException e) {
                logger.error("子订单[code={}]退款失败", orderDetail.getCode(), e);
            }
        } else {
            // 师傅不同意取消订单
            Integer orderStatus = orderDetail.getOrderStatus();
            // 修改主订单状态为原来的状态
            orderDetailMapper.updateStatus(orderDetailId, Math.abs(orderStatus));
        }
    }

    @Override
    public void updateByOrderMasterId(OrderDetail orderDetail) {
        orderDetailMapper.updateByOrderMasterId(orderDetail);
    }

    @Override
    public List<OrderDetail> selectByStatus(List<Integer> status) {
        Assert.isTrue(!CollectionUtils.isEmpty(status), "订单状态为空");
        return orderDetailMapper.selectByStatus(status);
    }

    /**
     * 子订单确认完成
     * 1.修改子订单状态为 OrderStatus.FINISH.code
     * 2.将子订单对应的子财务单确认分账
     * 3.异步提现至银行卡
     *
     * @param orderDetailId 子订单ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    // 暂时先用 synchronized 来避免重复提交
    public synchronized void finish(Long orderDetailId) throws BaseAdaPayException {
        // 更新订单状态
        orderDetailMapper.updateStatus(orderDetailId, OrderStatus.FINISH.code());
        // 校验订单
        OrderDetail orderDetail = selectById(orderDetailId);
        Assert.notNull(orderDetail, "找不到对应的子订单");
        Assert.isTrue(orderDetail.getOrderStatus().equals(OrderStatus.FINISH.code()), "找不到对应的子订单");
        FinancialMaster financialMaster = financialMasterService.selectByOrderMasterId(orderDetail.getOrderMasterId());
        Assert.notNull(financialMaster, "找不到主财务单");
        PaymentDTO payment = financialMasterService.selectPaymentById(financialMaster.getPaymentId());
        Assert.notNull(payment, "找不到支付记录");
        FinancialDetail financialDetail = financialDetailService.selectByOrderDetailId(orderDetailId);
        Assert.notNull(financialDetail, "找不到子财务单");
        Assert.isTrue(financialDetail.getPayStatus() == 1, "订单不是“已支付”状态");

        if (BigDecimal.ZERO.compareTo(financialDetail.getPayMoney()) > -1) {
            logger.info("子财务单[code={}]支付金额<=0，不需要分账", financialDetail.getCode());
            // 支付金额<=0的话 不需要走下面的流程了
            return;
        }

        // 上门师傅结单 要判断一下这个单是否已退款
        if (financialDetail.getPayStatus() > 1) {
            return;
        }
        // 分账账户ID
        String memberId;
        // 分账账户信息
        ArrayList<DivMember> divMembers = new ArrayList<>();
        String amount = AdapayUtils.bigDecimalToString(financialDetail.getPayMoney());
        memberId = AdapayUtils.getWorkerMemberId(financialDetail.getPayeeId(), orderDetail.getDeptId());
        DivMember divMember = new DivMember(memberId, amount, false);
        divMembers.add(divMember);

        logger.info("子订单[code={}]分账信息: {}", orderDetail.getCode(), JSON.toJSONString(divMembers));
        JSONObject response = adapayService.paymentConfirm(orderDetail.getDeptId(), payment.getId(), payment.getOrderNo(),
                amount, divMembers, null, null);
        logger.info("子订单[code={}]分账结果: {}", orderDetail.getCode(), response.toJSONString());

        boolean status = AdapayStatusEnum.pending.code.equals(response.getString("status")) ||
                AdapayStatusEnum.succeeded.code.equals(response.getString("status"));
        // 如果确认支付失败 这里抛出异常 回滚订单状态
        Assert.isTrue(status, response.getString("error_msg"));

        // 走到这里确认分账成功了 异步进入自动提现流程
        logger.info("子订单[code={}]开始自动提现", orderDetail.getCode());
        divMembers.forEach(member -> executor.execute(() -> {
            try {
                String orderNo = AdapayUtils.createOrderNo(AdapayOrderType.DRAW_CASH);
                JSONObject drawCashResponse = adapayService.drawCash(orderDetail.getDeptId(), orderNo, "T1",
                        amount, memberId, "订单结算", null);

                boolean drawCashStatus = AdapayStatusEnum.pending.code.equals(drawCashResponse.getString("status")) ||
                        AdapayStatusEnum.succeeded.code.equals(drawCashResponse.getString("status"));

                if (!drawCashStatus) {
                    //如果提现失败 把信息记录到error日志里
                    logger.error("自动发起提现失败: deptId={}, memberId={}, amount={}, 失败原因:{}", orderDetail.getDeptId(),
                            memberId, amount, drawCashResponse.getString("error_msg"));
                }
            } catch (BaseAdaPayException e) {
                logger.error("自动发起提现失败: orderDetailId={}, memberId={}, cashAmt={}", orderDetailId, memberId, amount, e);
            }
        }));
    }
}
