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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.springblade.common.base.BaseEntity;
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.DistanceUtils;
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.core.tool.api.R;
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.entity.DistributionConfig;
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.service.*;
import org.springblade.modules.finance.vo.OrderVO;
import org.springblade.modules.finance.mapper.OrderMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springblade.modules.groupon.entity.Groupon;
import org.springblade.modules.groupon.entity.SelfTake;
import org.springblade.modules.groupon.service.IGrouponService;
import org.springblade.modules.groupon.service.ISelfTakeService;
import org.springblade.modules.groupon.vo.SelfTakeVO;
import org.springblade.modules.groupon.wrapper.SelfTakeWrapper;
import org.springblade.modules.mall.entity.Store;
import org.springblade.modules.mall.service.IStoreService;
import org.springblade.modules.mall.wrapper.StoreWrapper;
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 com.baomidou.mybatisplus.core.metadata.IPage;
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.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
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;
	@Resource
	private IOrderGrouponGoodsService orderGrouponGoodsService;
	@Resource
	private IOrderGrouponService orderGrouponService;
	@Resource
	private ISelfTakeService selfTakeService;
	@Resource
	private OrderTimeoutService timeoutService;
	@Resource
	private IMessageService messageService;
	@Resource
	private IGrouponService grouponService;
	@Lazy
	@Resource
	private IAfterSalesService afterSalesService;
	@Resource
	private IDistributionConfigService distributionConfigService;
	@Resource
	private IStoreService storeService;
	@Lazy
	@Resource
	private ICommissionService commissionService;
	@Resource
	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<OrderGrouponGoods> goodsList = orderGrouponGoodsService.lambdaQuery().in(OrderGrouponGoods::getOrderId, collect).list();
				Map<Long, List<OrderGrouponGoods>> map = goodsList.stream().collect(Collectors.groupingBy(OrderGrouponGoods::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<OrderGrouponGoods> grouponGoods = null;
					if (AfterSalesTypeEnum.REFUND.getValue().equals(r.getType())) {
						grouponGoods = orderGrouponGoodsService.lambdaQuery().eq(OrderGrouponGoods::getOrderId, r.getOrderId()).list();
					} else if (AfterSalesTypeEnum.RETURN_GOODS.getValue().equals(r.getType())) {
						grouponGoods = orderGrouponGoodsService.lambdaQuery().eq(BaseEntity::getId, r.getOrderGrouponGoodsId()).list();
					} else if (AfterSalesTypeEnum.PART_REFUND.getValue().equals(r.getType()) ||
							AfterSalesTypeEnum.NOT_ARRIVE_REFUND.getValue().equals(r.getType())) {
						grouponGoods = afterSalesGoodsService.getGoodsByAfterSalesId(r.getAfterSalesId());
						for (OrderGrouponGoods orderGrouponGoods : grouponGoods) {
							orderGrouponGoods.setGoodsNum(orderGrouponGoods.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 (OrderGrouponGoods orderGrouponGoods : response.getGoodsList()) {
				// 判断是否有未申请售后的
				if (YesOrNoEnum.NO.getValue().equals(orderGrouponGoods.getIsAfterSales())) {
					flag = true;
					break;
				}
			}
        }
		return flag;
	}

	private boolean getCanReturnGoods(UserOrderResponse response) {
		boolean flag = false;
		if (CollUtil.isNotEmpty(response.getGoodsList())) {
			for (OrderGrouponGoods orderGrouponGoods : response.getGoodsList()) {
				if (YesOrNoEnum.NO.getValue().equals(orderGrouponGoods.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);

		SelfTake selfTake = selfTakeService.getById(detail.getSelfTakeId());
		if (selfTake != null) {
			SelfTakeVO selfTakeVO = SelfTakeWrapper.build().entityVO(selfTake);
			detail.setSelfTake(selfTakeVO);
			double distance = 0.0;
			if (longitude != null && latitude != null) {
				distance = DistanceUtils.getDistance(longitude, latitude, selfTake.getLongitude(), selfTake.getLatitude());
			}
			selfTakeVO.setDistance(distance);
		}
		// 待支付的订单需计算支付剩余时间
		if (OrderStateEnum.WAIT_PAY.getValue().equals(detail.getOrderState())) {
			long timeoutTime = timeoutService.getTimeoutTime();
			// 计算当前时间跟下单时间相差的秒数
			LocalDateTime now = LocalDateTime.now();
			long seconds = LocalDateTimeUtil.between(detail.getCreateTime(), now).getSeconds();
			detail.setRemainTime(timeoutTime - seconds);
		}

		if (afterSalesId != null) {
			AfterSales afterSales = afterSalesService.getById(afterSalesId);
			detail.setAfterSalesReason(afterSales.getReason());
			detail.setRefundApplyTime(afterSales.getCreateTime());
			detail.setRefundNo(afterSales.getAfterSalesNo());
			if (AfterSalesStatusEnum.FINISH.getValue().equals(afterSales.getStatus())) {
				detail.setAfterSalesDesc("已退款");
			} else {
				detail.setAfterSalesDesc(AfterSalesStatusEnum.getNameByValue(afterSales.getStatus()));
			}
			List<OrderGrouponGoods> list;
			if (AfterSalesTypeEnum.REFUND.getValue().equals(afterSales.getType())) {
				detail.setRefundAmount(detail.getPaymentAmount());
				list = orderGrouponGoodsService.lambdaQuery().eq(OrderGrouponGoods::getOrderId, orderId).list();
			} else if (AfterSalesTypeEnum.PART_REFUND.getValue().equals(afterSales.getType()) || AfterSalesTypeEnum.NOT_ARRIVE_REFUND.getValue().equals(afterSales.getType())) {
				list = afterSalesGoodsService.getGoodsByAfterSalesId(afterSalesId);
				BigDecimal totalRefundAmount = BigDecimal.valueOf(0);
				for (OrderGrouponGoods orderGrouponGoods : list) {
					totalRefundAmount = totalRefundAmount.add(orderGrouponGoods.getGoodsPrice().multiply(BigDecimal.valueOf(orderGrouponGoods.getRefundGoodsNum())));
				}
				detail.setRefundAmount(totalRefundAmount);
			} else {
				OrderGrouponGoods goods = orderGrouponGoodsService.getById(afterSales.getOrderGrouponGoodsId());
				detail.setRefundAmount(goods.getGoodsPrice().multiply(BigDecimal.valueOf(afterSales.getReturnGoodsNum())));
				list = orderGrouponGoodsService.lambdaQuery().eq(BaseEntity::getId, afterSales.getOrderGrouponGoodsId()).list();
				if (AfterSalesStatusEnum.PROCESSED.getValue().equals(afterSales.getStatus())) {
					// 查询供应商信息
					OrderGrouponGoods orderGrouponGoods = orderGrouponGoodsService.getById(afterSales.getOrderGrouponGoodsId());
					Store store = storeService.getById(orderGrouponGoods.getSupplierId());
					detail.setStore(StoreWrapper.build().entityVO(store));
				}
			}

			detail.setAfterSales(afterSales);
			detail.setGoodsList(list);
		} else {
			List<OrderGrouponGoods> list = orderGrouponGoodsService.lambdaQuery().eq(OrderGrouponGoods::getOrderId, orderId).list();
			detail.setGoodsList(list);
		}
		return detail;
	}

	@Override
	public void modifySelfTake(Long orderId, Long selfTakeId) {
		OrderGroupon one = orderGrouponService.lambdaQuery().eq(OrderGroupon::getOrderId, orderId).one();
		Assert.notNull(one, "数据不存在");
		SelfTake selfTake = selfTakeService.getById(selfTakeId);
		Assert.notNull(selfTake, "自提点数据不存在");
		one.setSelfTakeId(selfTakeId);
		orderGrouponService.updateById(one);
	}

	@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, "订单数据不存在");

		OrderGroupon orderGroupon = orderGrouponService.lambdaQuery().eq(OrderGroupon::getOrderId, order.getId()).one();
		Assert.notNull(orderGroupon, "数据不存在");
		Groupon groupon = grouponService.getById(orderGroupon.getGrouponId());
		Assert.notNull(groupon, "团购数据不存在");

		if (YesOrNoEnum.YES.getValue().equals(orderGroupon.getIsArrive()) &&
				!(OrderStateEnum.WAIT_DELIVERY.getValue().equals(order.getOrderState()) &&
						groupon.getGrouponEndTime().isAfter(LocalDateTime.now()))) {
			throw new ServiceException("当前订单不支持退款");
		}


		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 totalAmount, 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, totalAmount, 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);
	}
}
