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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springblade.common.base.BaseEntity;
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.core.tool.api.R;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.modules.api.request.ModifyAddressRequest;
import org.springblade.modules.api.service.SubmitOrderService;
import org.springblade.modules.api.vo.LogisticDetailVO;
import org.springblade.modules.api.vo.LogisticVO;
import org.springblade.modules.api.vo.OrderStatisNum;
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.mall.service.IGoodsSpecService;
import org.springblade.modules.platform.entity.UserAddress;
import org.springblade.modules.platform.service.IUserAddressService;
import org.springblade.modules.platform.service.IUserMessageService;
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.IOException;
import java.io.Serializable;
import java.util.*;
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;
	@Lazy
	@Resource
	private IAliPayService aliPayService;
	@Lazy
	@Resource
	private IWxPayService wxPayService;
	@Resource
	private IOrderRefundService orderRefundService;
	@Resource
	private IOrderDetailService orderDetailService;
	@Resource
	private IUserAddressService userAddressService;
	@Resource
	private ITransactionFlowService transactionFlowService;
	@Resource
	private IGoodsSpecService goodsSpecService;
	@Resource
	private IOrderGoodsService orderGoodsService;
	@Resource
	private BladeRedis bladeRedis;
	@Resource
	private HuaWeiApiService huaWeiApiService;
	@Resource
	private IUserMessageService userMessageService;

	@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));
	}

	@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);

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

			// 移除订单超时缓存
			bladeRedis.del(CacheBizConstant.ORDER_TIMEOUT_PREFIX + order.getId());

			// 发送用户支付成功消息
			userMessageService.sendMessage("订单支付成功", order.getId(), MessageTypeEnum.TRADE.getValue(), order.getCreateUserId());
		} 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) throws IOException {
		OrderDetail orderDetail = orderDetailService.lambdaQuery().eq(OrderDetail::getOrderId, orderId).one();
		Assert.notNull(orderDetail, "订单数据不存在");
		LogisticVO logisticVO = new LogisticVO();
		if (StrUtil.isNotBlank(orderDetail.getLogisticNo())) {
			String url = "http://wuliu.apistore.huaweicloud.com/wuliu?no=" + orderDetail.getLogisticNo().trim();
			String result = huaWeiApiService.get(url);
			JSONObject json = JSON.parseObject(result);
			String status = json.getString("status");
			if ("0".equals(status)) {
				JSONObject jsonObject = json.getJSONObject("result");
				logisticVO.setExpressCompany(jsonObject.getString("expName"));
				logisticVO.setWaybillNumber(jsonObject.getString("number"));
				logisticVO.setState(jsonObject.getInteger("deliverystatus"));
				logisticVO.setStateDesc(this.getLogisticState(logisticVO.getState()));
				JSONArray list = jsonObject.getJSONArray("list");
				List<LogisticDetailVO> logisticDetailVOS = list.toJavaList(LogisticDetailVO.class);
				logisticVO.setLogisticDetail(logisticDetailVOS);
			} else {
				throw new ServiceException(json.getString("msg"));
			}
		} else {
			throw new ServiceException("该订单暂未绑定物流单号");
		}
		return logisticVO;
	}


	private String getLogisticState(Integer state) {
		if (state == 0) {
			return "快递收件(揽件)";
		} else if(state == 1) {
			return "在途中";
		} else if(state == 2) {
			return "正在派件";
		} else if(state == 3) {
			return "已签收";
		} else if(state == 4) {
			return "派送失败";
		} else if(state == 5) {
			return "疑难件";
		} else if(state == 6) {
			return "退件签收";
		} else {
			return "未知";
		}
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void modifyAddress(ModifyAddressRequest request) {
		Order order = this.getById(request.getId());
		Assert.notNull(order, "订单数据不存在");
		UserAddress userAddress = userAddressService.getById(request.getUserAddressId());
		Assert.notNull(userAddress, "未查询到用户地址数据");
		if (OrderStateEnum.WAIT_DELIVERY.getValue().equals(order.getOrderState())) {
			OrderDetail orderDetail = orderDetailService.getByOrderId(order.getId());
			Assert.notNull(orderDetail, "未查询到订单详情数据");
			orderDetail.setProvince(userAddress.getProvince());
			orderDetail.setCity(userAddress.getCity());
			orderDetail.setDistrict(userAddress.getDistrict());
			orderDetail.setStreet(userAddress.getStreet());
			orderDetail.setAddress(userAddress.getDetail());
			orderDetailService.updateById(orderDetail);

			order.setConsignee(userAddress.getRealName());
			order.setConsigneePhoneNumber(userAddress.getPhone());
			updateById(order);
		} else {
			throw new ServiceException("当前订单不支持修改地址");
		}
	}

	@Transactional(rollbackFor = Exception.class)
	@Async
	@Override
	public void orderTimeout(Long orderId, Long transactionId) {
		try {
			if (orderId != null) {
				Order order = this.getById(orderId);
				if (!OrderStateEnum.WAIT_PAY.getValue().equals(order.getOrderState())) {
					return;
				}
				// 修改订单状态为已取消
				order.setOrderState(OrderStateEnum.CANCEL.getValue());
				order.setUpdateTime(DateUtil.now());
				this.updateById(order);
				// 释放商品库存
				this.releaseGoodsStock(order.getOrderNo());
			} else {
				TransactionFlow flow = transactionFlowService.getById(transactionId);
				flow.setStatus(TransactionStatusEnum.CANCEL.getValue());
				flow.setUpdateTime(DateUtil.now());
				transactionFlowService.updateById(flow);
				if (!TransactionStatusEnum.NOT_PAY.getValue().equals(flow.getStatus())) {
					return;
				}
				List<Order> list = this.lambdaQuery().eq(Order::getTransactionNo, flow.getTransactionNo()).list();
				for (Order order : list) {
					// 修改订单状态为已取消
					order.setOrderState(OrderStateEnum.CANCEL.getValue());
					order.setUpdateTime(DateUtil.now());
					// 释放商品库存
					this.releaseGoodsStock(order.getOrderNo());
				}
				this.updateBatchById(list);
			}
		} catch (Exception e) {
			log.error("处理订单超时失败_" + e.getMessage(), e);
			logger.error("处理订单超时失败", "orderId:" + orderId + ",transactionId:" + transactionId);
			throw e;
		}
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void deliverGoods(Long orderId, String wlCompany, String wlNo) {
		Order order = baseMapper.selectById(orderId);
		Assert.notNull(order, "订单数据不存在");
		OrderDetail orderDetail = orderDetailService.getByOrderId(orderId);
		Assert.notNull(orderDetail, "订单详细数据不存在");
		orderDetail.setLogisticNo(wlNo);
		orderDetail.setLogisticsCompany(wlCompany);
		orderDetailService.updateById(orderDetail);
		order.setOrderState(OrderStateEnum.WAIT_RECEIVE.getValue());
		this.updateById(order);
	}

	@Override
	public List<OrderStatisNum> statisNum(Long userId) {
		List<OrderStatisNum> orderStatisNumList = new ArrayList<>();
		List<Order> list = lambdaQuery().eq(BaseEntity::getCreateUserId, userId).ne(Order::getOrderState, OrderStateEnum.CANCEL.getValue()).list();
		for (Order order : list) {
			if (OrderStateEnum.REFUNDING.getValue().equals(order.getOrderState()) || OrderStateEnum.REFUNDED.getValue().equals(order.getOrderState())) {
				order.setOrderState(5);
			}
		}
		if (CollUtil.isNotEmpty(list)) {
			Map<Integer, List<Order>> collect = list.stream().collect(Collectors.groupingBy(Order::getOrderState));
			collect.forEach((k, v) -> {
				OrderStatisNum statisNum = new OrderStatisNum(v.size(), k, k == 5 ? "售后" : OrderStateEnum.getNameByValue(k));
				orderStatisNumList.add(statisNum);
			});
		}
		return orderStatisNumList;
	}

	@Override
	public List<Order> queryTimeoutOrder(long seconds) {
		return baseMapper.selectTimeoutOrder(seconds);
	}

	@Override
	public void cancel(Order record) {
		if (OrderStateEnum.WAIT_PAY.getValue().equals(record.getOrderState())) {
			Order order = new Order();
			order.setId(record.getId());
			order.setOrderState(OrderStateEnum.CANCEL.getValue());
			this.updateById(order);

			// 发送用户支付成功消息
			userMessageService.sendMessage("订单已取消", record.getId(), MessageTypeEnum.TRADE.getValue(), record.getCreateUserId());
		} else {
			throw new ServiceException("当前订单不支持取消");
		}
	}

	@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.now());
		baseMapper.updateById(order);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void orderRefund(Long id) {
		Order order = this.getById(id);
		if (order == null) {
			throw new ServiceException("订单数据不存在");
		}
		if (!OrderStateEnum.WAIT_DELIVERY.getValue().equals(order.getOrderState())) {
			throw new ServiceException("当前状态不支持退款");
		}
		Integer orderState = order.getOrderState();
		logger.info("发起退款", "订单号：" + order.getOrderNo() + "订单状态");
		OrderRefund orderRefund = OrderRefund.build(order.getId());
		order.setOrderState(OrderStateEnum.REFUNDING.getValue());
		order.setIsAftersales(YesOrNoEnum.YES.getValue());
		// 更新订单状态信息
		this.updateById(order);
		// 保存退款记录
		orderRefundService.save(orderRefund);
		// 如果订单未发货，直接给用户退款
		if (OrderStateEnum.WAIT_DELIVERY.getValue().equals(orderState)) {
			this.commonRefund(order);
		}
	}

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

	@Override
	public Map<String, Object> dataReport() {
		Map<String, Object> result = new HashMap<>();
		result.put("totalOrderCount", this.baseMapper.selectTotalOrderCount());
		result.put("totalOrderAmount", this.baseMapper.selectTotalOrderAmount());
		result.put("totalOrderCountToday", this.baseMapper.selectTotalOrderCountToday());
		result.put("totalOrderAmountToday", this.baseMapper.selectTotalOrderAmountToday());
		return result;
	}

	@Override
	public Map<String, Object> dataReportByDate(String queryType) {
		Map<String, Object> result = new HashMap<>();
		if ("full-year".equals(queryType)) {
			convertStitc(this.baseMapper.selectCurrYear(), result);
		} else if ("nearly_one".equals(queryType)) {
			convertStitc(this.baseMapper.selectStaticByDay(60), result);
		} else if ("nearly_tow".equals(queryType)) {
			convertStitc(this.baseMapper.selectStaticByDay(30), result);
		} else if ("week".equals(queryType)) {
			convertStitc(this.baseMapper.selectStaticByDay(7), result);
		}
		return result;
	}

	private void convertStitc(List<Map<String, Object>> maps, Map<String, Object> result) {
		List<Object> desc = new ArrayList<>();
		List<Object> totalCount = new ArrayList<>();
		List<Object> totalAmount = new ArrayList<>();
		for (Map<String, Object> map : maps) {
			desc.add(map.get("dateValue"));
			totalCount.add(map.get("totalCount"));
			totalAmount.add(map.get("totalAmount"));
		}
		result.put("desc", desc);
		result.put("totalCount", totalCount);
		result.put("totalAmount", totalAmount);
	}

	/**
	 * 获取结算账号
	 * @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 "";
	}

	/**
	 * 释放商品库存
	 * @param orderNo
	 */
	private void releaseGoodsStock(String orderNo) {
		OrderGoods orderGoods = orderGoodsService.getByOrderNo(orderNo);
		Assert.notNull(orderGoods, "未查询到商品关联数据_" + orderNo);
		goodsSpecService.addStock(Long.parseLong(orderGoods.getGoodsSpecIds()), orderGoods.getGoodsNum());
	}
}
