package org.springblade.modules.finance.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springblade.common.cache.ParamCache;
import org.springblade.common.constant.CacheBizConstant;
import org.springblade.common.constant.CommonConstant;
import org.springblade.common.enums.*;
import org.springblade.common.utils.CommonUtil;
import org.springblade.common.utils.SpringUtils;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.log.logger.BladeLogger;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.modules.api.response.UserOrderDetailResponse;
import org.springblade.modules.api.response.UserOrderResponse;
import org.springblade.modules.api.service.SubmitOrderService;
import org.springblade.modules.api.vo.LogisticVO;
import org.springblade.modules.distribution.service.ICommissionService;
import org.springblade.modules.distribution.service.IDistributionConfigService;
import org.springblade.modules.finance.entity.*;
import org.springblade.modules.finance.mapper.OrderMapper;
import org.springblade.modules.finance.service.*;
import org.springblade.modules.finance.vo.OrderVO;
import org.springblade.modules.mall.service.IStoreService;
import org.springblade.modules.platform.service.IMessageService;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 订单主表 服务实现类
 *
 * @author BladeX
 * @since 2023-02-14
 */
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

	@Resource
	private BladeLogger logger;
	@Lazy
	@Resource
	private IOrderExceptionService orderExceptionService;
	@Resource
	private BladeRedis bladeRedis;
	@Lazy
	@Resource
	private IWxPayService wxPayService;
	@Resource
	private IOrderRefundService orderRefundService;
	@Resource
	private ITransactionFlowService transactionFlowService;
	@Lazy
	@Resource
	private IOrderGoodsService orderGoodsService;
	@Resource
	private OrderTimeoutService timeoutService;
	@Resource
	private IMessageService messageService;
	@Lazy
	@Resource
	private IAfterSalesService afterSalesService;
	@Resource
	private IDistributionConfigService distributionConfigService;
	@Resource
	private IStoreService storeService;
	@Lazy
	@Resource
	private ICommissionService commissionService;
	@Resource
	@Lazy
	private IAfterSalesGoodsService afterSalesGoodsService;

	@Override
	public Order init() {
		Order order = new Order();
		order.setOrderNo(CommonUtil.genTimeID());
		order.setOrderState(OrderStateEnum.WAIT_PAY.getValue());
		order.setIsCart(YesOrNoEnum.NO.getValue());
		order.setPaymentState(PaymentStateEnum.UN_PAID.getValue());
		order.setIsAftersales(YesOrNoEnum.NO.getValue());
		order.setInvoiceState(InvoiceStateEnum.UN_APPLY.getValue());
		return order;
	}

	@Override
	public IPage<OrderVO> selectOrderPage(IPage<OrderVO> page, OrderVO order) {
		return page.setRecords(baseMapper.selectOrderPage(page, order));
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public Order orderCancel(Long orderId) {
		Order record = getById(orderId);
		if (record == null) {
			throw new ServiceException("订单数据不存在");
		}
		if (OrderStateEnum.WAIT_PAY.getValue().equals(record.getOrderState())) {
			Order order = new Order();
			order.setId(record.getId());
			order.setOrderState(OrderStateEnum.CANCEL.getValue());
			updateById(order);
			// 释放商品库存
			this.releaseGoodsStock(record);
			// 发送消息
			messageService.sendMessage(record.getUserId(), "订单已取消", record.getOrderNo());
			// 从超时队列中移除该订单
			bladeRedis.zRem(CacheBizConstant.TIMEOUT_ORDER_LIST, orderId);
			return record;
		} else {
			throw new ServiceException("当前订单不支持取消");
		}
	}

	@Override
	public Order getByOrderNo(String orderNo) {
		return this.lambdaQuery().eq(Order::getOrderNo, orderNo).one();
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void orderPaySuccess(Order order, String payType) {
		String orderNo = order.getOrderNo();
		try {
			if(!OrderStateEnum.WAIT_PAY.getValue().equals(order.getOrderState())){
				log.info("订单号异常不能支付，订单号：" + orderNo + ",当前状态:" + order.getOrderState());
				throw new ServiceException("订单号异常不能支付，订单号：" + orderNo + ",当前状态:" + order.getOrderState());
			}
			order.setOrderState(OrderStateEnum.WAIT_DELIVERY.getValue());
			order.setPaymentMode(payType);
			order.setPaymentTime(new Date());
			order.setPaymentState(PaymentStateEnum.PAID.getValue());
			order.setLogisticsState(LogisticsStateEnum.UNDELIVERED.getValue());
			order.setSettlementAccount(this.getSettlementAccount(payType));
			this.updateById(order);

			// 记录流水
			TransactionFlow flow = new TransactionFlow();
			flow.setTransactionNo(orderNo);
			flow.setAmount(order.getPaymentAmount());
			flow.setTranType(1);// 收入
			flow.setPaymentTime(new Date());
			flow.setStatus(2);// 支付成功
			flow.setPaymentType(payType);
			transactionFlowService.save(flow);

			SubmitOrderService submitOrderService = SpringUtils.getBean(OrderTypeEnum.getInstanceByValue(order.getOrderType()));
			submitOrderService.paySuccessAfter(order);

			// 移除订单超时缓存
//			bladeRedis.del(CacheBizConstant.ORDER_TIMEOUT_PREFIX + order.getId());
			bladeRedis.zRem(CacheBizConstant.TIMEOUT_ORDER_LIST, order.getId());
		} catch (Exception e) {
			log.error("订单支付成功回调异常, 订单号:" + orderNo + e.getMessage(), e);
			logger.error("订单支付成功回调异常", orderNo + e.getMessage());
			orderExceptionService.saveExceptionOrder(order, e.getMessage(), payType, e);
		}
	}

	@Override
	public LogisticVO logistic(Long orderId) {
		LogisticVO logisticVO = new LogisticVO();
		// todo ...
		return logisticVO;
	}


	@Async
	@Override
	public void orderTimeout(Long orderId, Long transactionId) {
		try {
			if (orderId != null) {
				Order order = this.orderCancel(orderId);
			}
		} catch (Exception e) {
			e.printStackTrace();
			log.error("处理订单超时失败_" + e.getMessage(), e);
			logger.error("处理订单超时失败", "orderId:" + orderId + ",transactionId:" + transactionId);
			throw e;
		}
	}

	@Override
	public IPage<UserOrderResponse> getUserOrder(IPage<UserOrderResponse> page, Long userId, Integer orderState) {
		List<UserOrderResponse> list;
		if (!OrderStateEnum.AFTER_SALES.getValue().equals(orderState)) {
			list = baseMapper.getUserOrder(page, userId, orderState);
			if (CollUtil.isNotEmpty(list)) {
				List<Long> collect = list.stream().map(UserOrderResponse::getOrderId).collect(Collectors.toList());
				List<OrderGoods> goodsList = orderGoodsService.lambdaQuery().in(OrderGoods::getOrderId, collect).list();
				Map<Long, List<OrderGoods>> map = goodsList.stream().collect(Collectors.groupingBy(OrderGoods::getOrderId));
				for (UserOrderResponse response : list) {
					response.setGoodsList(map.get(response.getOrderId()));
					response.setCanReturnGoods(this.getCanReturnGoods(response));
					response.setCanRefund(this.getCanRefund(response));
					response.setCanRefundTwo(this.getCanRefundTwo(response));
				}
			}
		} else {
			list = baseMapper.getUserAfterSalesOrder(page, userId, orderState);
			if (CollUtil.isNotEmpty(list)) {
				for (UserOrderResponse r : list) {
					List<OrderGoods> grouponGoods = null;
					if (AfterSalesTypeEnum.REFUND.getValue().equals(r.getType())) {
						grouponGoods = orderGoodsService.lambdaQuery().eq(OrderGoods::getOrderId, r.getOrderId()).list();
					}else if (AfterSalesTypeEnum.NOT_ARRIVE_REFUND.getValue().equals(r.getType())) {
						grouponGoods = afterSalesGoodsService.getGoodsByAfterSalesId(r.getAfterSalesId());
						for (OrderGoods orderGoods : grouponGoods) {
							orderGoods.setGoodsNum(orderGoods.getRefundGoodsNum());
						}
					}
					r.setGoodsList(grouponGoods);
					r.setAfterSalesStatusDesc(AfterSalesStatusEnum.getNameByValue(r.getAfterSalesStatus()));
					if (AfterSalesStatusEnum.FINISH.getValue().equals(r.getAfterSalesStatus())) {
						r.setAfterSalesStatusDesc("已退款");
					}
				}
			}
		}
		return page.setRecords(list);
	}


	public boolean getCanRefundTwo(UserOrderResponse response) {
		if (DateUtil.date().isAfter(response.getTakeStartTime()) && YesOrNoEnum.NO.getValue().equals(response.getIsArrive())) {
			Integer count = afterSalesService.lambdaQuery().eq(AfterSales::getOrderId, response.getOrderId())
					.eq(AfterSales::getType, AfterSalesTypeEnum.NOT_ARRIVE_REFUND.getValue()).count();
			return count < 1;
		} else {
			return false;
		}
	}

	private boolean getCanRefund(UserOrderResponse response) {
		boolean flag = false;
		// 团购结束前可以申请部分退款
		if (OrderStateEnum.WAIT_DELIVERY.getValue().equals(response.getOrderState()) && DateUtil.date().before(response.getGrouponEndTime())) {
			for (OrderGoods orderGoods : response.getGoodsList()) {
				// 判断是否有未申请售后的
				if (YesOrNoEnum.NO.getValue().equals(orderGoods.getIsAfterSales())) {
					flag = true;
					break;
				}
			}
        }
		return flag;
	}

	private boolean getCanReturnGoods(UserOrderResponse response) {
		boolean flag = false;
		if (CollUtil.isNotEmpty(response.getGoodsList())) {
			for (OrderGoods orderGoods : response.getGoodsList()) {
				if (YesOrNoEnum.NO.getValue().equals(orderGoods.getIsAfterSales())) {
					flag = true;
					break;
				}
			}
		}
		int afterSalesDays = Integer.parseInt(ParamCache.getValueDefault(CacheBizConstant.ORDER_AFTER_SALES_DAYS, "3"));
		// 根据团购提货开始时间计算售后时间
		long day = DateUtil.betweenDay(response.getTakeStartTime(), new Date(), false);
		return OrderStateEnum.COMPLETED.getValue().equals(response.getOrderState()) &&
				day <= afterSalesDays && flag;
	}

	@Override
	public UserOrderDetailResponse getUserOrderDetail(Long orderId, Long afterSalesId, Double longitude, Double latitude) {
		UserOrderDetailResponse detail = baseMapper.getUserOrderDetail(orderId);

		return detail;
	}


	@Override
	public Page<Order> getOrderByExpirationTime(Page page, Date time) {
		return this.getBaseMapper().getOrderByExpirationTime(page,time);
	}

	@Override
	public void confirm(Serializable id) {
		Order order = baseMapper.selectById(id);
		Assert.notNull(order, "订单数据不存在");
		//必须是支付状态为待收货
		if (!OrderStateEnum.WAIT_RECEIVE.getValue().equals(order.getOrderState())){
			throw new ServiceException("当前状态不能确认收货!");
		}
		order.setOrderState(OrderStateEnum.COMPLETED.getValue()); //已完成
		order.setRecordTime(DateUtil.date());
		baseMapper.updateById(order);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void orderRefund(Long id) {
		Order order = this.getById(id);
		Assert.notNull(order, "订单数据不存在");


		logger.info("发起退款", "订单号：" + order.getOrderNo() + "订单状态");
		order.setOrderState(OrderStateEnum.AFTER_SALES.getValue());
		order.setIsAftersales(YesOrNoEnum.YES.getValue());
		// 更新订单状态信息
		this.updateById(order);
		// 记录售后数据
		afterSalesService.add(order.getId(), "退款", AfterSalesTypeEnum.REFUND);

		// 将分销收益改为无效
		commissionService.invalidCommission(order.getId());
	}

	@Override
	public void commonRefund(Order order, Long afterSalesId) {
		// 支付宝退款
		if (CommonConstant.ALI_PAY.equals(order.getPaymentMode())) {
//			aliPayService.refund(order);
		} else if (CommonConstant.WX_PAY.equals(order.getPaymentMode())) { // 微信退款
			wxPayService.orderRefund(order, afterSalesId);
		} else {
			throw new ServiceException("暂不支持该退款方式" + order.getPaymentMode());
		}
	}

	@Override
	public void commonRefund(String orderNo, Long orderId, BigDecimal refundAmount, BigDecimal paymentAmount, Long afterSalesId) {
		OrderRefund orderRefund = orderRefundService.lambdaQuery().eq(OrderRefund::getAfterSalesId, afterSalesId)
				.eq(OrderRefund::getRefundState, RefundStateEnum.ONLY_REFUND.getValue()).one();
		if (orderRefund != null) {
			throw new ServiceException("申请中，请勿重复操作");
		}
		wxPayService.orderRefund(orderNo, orderId, refundAmount, paymentAmount, afterSalesId);
	}

	/**
	 * 获取结算账号
	 * @param paymentMode 支付方式
	 */
	private String getSettlementAccount(String paymentMode) {
//		if (CommonConstant.ALI_PAY.equals(paymentMode)) {
//			return aliPayService.getPayeeUserId();
//		}
		if (CommonConstant.WX_PAY.equals(paymentMode)) {
			return wxPayService.getMchId();
		}
		return "";
	}

	/**
	 * 释放商品库存
	 */
	private void releaseGoodsStock(Order order) {
		SubmitOrderService submitOrderService = SpringUtils.getBean(OrderTypeEnum.getInstanceByValue(order.getOrderType()));
		submitOrderService.releaseGoodsStock(order);
	}
}
