/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.modules.finance.service.impl;

import cn.hutool.core.collection.CollUtil;
import org.springblade.common.base.BaseEntity;
import org.springblade.common.cache.ParamCache;
import org.springblade.common.constant.CacheBizConstant;
import org.springblade.common.enums.*;
import org.springblade.common.utils.CommonUtil;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.modules.api.request.AfterSalesRequest;
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.AfterSalesVO;
import org.springblade.modules.finance.mapper.AfterSalesMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springblade.modules.finance.vo.AppointGoodsVO;
import org.springblade.modules.finance.wrapper.AfterSalesWrapper;
import org.springblade.modules.finance.wrapper.OrderWrapper;
import org.springblade.modules.groupon.entity.Groupon;
import org.springblade.modules.groupon.service.IGrouponService;
import org.springblade.modules.mall.service.IStoreService;
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.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 售后表 服务实现类
 *
 * @author BladeX
 * @since 2023-06-15
 */
@Service
public class AfterSalesServiceImpl extends ServiceImpl<AfterSalesMapper, AfterSales> implements IAfterSalesService {

	@Resource
	private IOrderGoodsService orderGoodsService;
	@Resource
	private IOrderService orderService;
	@Resource
	private IOrderGrouponService orderGrouponService;
	@Resource
	private IOrderRefundService orderRefundService;
	@Resource
	private IStoreService storeService;
	@Resource
	private ICommissionService commissionService;
	@Resource
	private IDistributionConfigService distributionConfigService;
	@Resource
	private IGrouponService grouponService;
	@Resource
	private IAfterSalesGoodsService afterSalesGoodsService;
	@Resource
	private IAppointGoodsService appointGoodsService;

	@Override
	public IPage<AfterSalesVO> selectAfterSalesPage(IPage<AfterSalesVO> page, AfterSalesVO afterSales) {
		return page.setRecords(baseMapper.selectAfterSalesPage(page, afterSales));
	}

	@Transactional
	@Override
	public void add(Long orderId, String reason, AfterSalesTypeEnum typeEnum) {
		Order order = orderService.getById(orderId);
		Assert.notNull(order, "订单数据不存在");

		OrderGroupon orderGroupon = orderGrouponService.lambdaQuery().eq(OrderGroupon::getOrderId, orderId).one();
		Assert.notNull(orderGroupon, "orderGroupon数据不存在");

		AfterSales afterSales = init();
		afterSales.setOrderId(orderId);
		afterSales.setReason(reason);
		afterSales.setType(typeEnum.getValue());
		afterSales.setUserId(AuthUtil.getUserId());
		afterSales.setGrouponId(orderGroupon.getGrouponId());
		save(afterSales);

		orderGoodsService.lambdaUpdate().set(OrderGoods::getIsAfterSales, YesOrNoEnum.YES.getValue())
				.eq(OrderGoods::getOrderId, orderId).update();
	}

	@Override
	public void refund(Long afterSalesId) {
		AfterSales afterSales = getById(afterSalesId);
		Assert.notNull(afterSales, "售后信息不存在");
		afterSales.setStatus(AfterSalesStatusEnum.FINISH.getValue());
		afterSales.setRefundState(YesOrNoEnum.YES.getValue());
		afterSales.setUpdateTime(new Date());
		updateById(afterSales);
	}

	@Transactional
	@Override
	public void submit(AfterSalesRequest request) {
		Order order = orderService.getById(request.getOrderId());
		Assert.notNull(order, "订单数据不存在");

		order.setIsAftersales(YesOrNoEnum.YES.getValue());
		orderService.updateById(order);
		OrderGroupon orderGroupon = orderGrouponService.lambdaQuery().eq(OrderGroupon::getOrderId, request.getOrderId()).one();
		Assert.notNull(orderGroupon, "orderGroupon数据不存在");
		Groupon groupon = grouponService.getById(orderGroupon.getGrouponId());
		Assert.notNull(orderGroupon, "groupon数据不存在");

		int afterSalesDays = Integer.parseInt(ParamCache.getValueDefault(CacheBizConstant.ORDER_AFTER_SALES_DAYS, "3"));
		// 校验是否能申请售后
		LocalDateTime time = groupon.getTakeStartTime().plusDays(afterSalesDays);
		if (time.isBefore(LocalDateTime.now())) {
			throw new ServiceException("已过售后期");
		}

		// 校验商品是否已经申请过售后
		List<Long> collect = request.getAfterGoodsList().stream().map(BaseEntity::getId).collect(Collectors.toList());
		List<OrderGoods> list = orderGoodsService.lambdaQuery().in(BaseEntity::getId, collect).list();
		for (OrderGoods orderGoods : list) {
			if (YesOrNoEnum.YES.getValue().equals(orderGoods.getIsAfterSales())) {
				throw new ServiceException("商品【"+orderGoods.getGoodsName()+"】已申请过售后");
			}
			orderGoods.setIsAfterSales(YesOrNoEnum.YES.getValue());
		}
		// 更新售后状态
		orderGoodsService.updateBatchById(list);

		Map<Long, OrderGoods> map = request.getAfterGoodsList().stream().collect(Collectors.toMap(BaseEntity::getId, Function.identity()));
		List<AfterSales> afterSalesList = new ArrayList<>();
		for (OrderGoods goods : list) {
			AfterSales afterSales = init();
			afterSales.setOrderId(request.getOrderId());
			afterSales.setReason(request.getReason());
			afterSales.setType(request.getAfterSalesType());
			afterSales.setOrderGoodsId(goods.getId());

			OrderGoods o = map.get(goods.getId());
			afterSales.setReturnGoodsNum(o.getGoodsNum());
			afterSales.setIsArrived(YesOrNoEnum.NO.getValue());
			afterSales.setUserId(AuthUtil.getUserId());
			afterSales.setStoreId(goods.getShopId());
			afterSales.setGrouponId(orderGroupon.getGrouponId());
			afterSalesList.add(afterSales);
		}
		saveBatch(afterSalesList);

		// TODO: 2024/7/29   将分销收益改为无效，所有售后结束后重新计算收益
		//commissionService.invalidCommission(order.getId());
	}

	@Override
	public void cancel(String afterSalesNo) {
		AfterSales afterSales = lambdaQuery().eq(AfterSales::getAfterSalesNo, afterSalesNo).one();
		Assert.notNull(afterSales, "售后数据不存在");
		afterSales.setStatus(AfterSalesStatusEnum.CLOSE.getValue());
		updateById(afterSales);
	}

	@Override
	public void agree(String afterSalesNo) {
		AfterSales afterSales = lambdaQuery().eq(AfterSales::getAfterSalesNo, afterSalesNo).one();
		Assert.notNull(afterSales, "售后数据不存在");
		afterSales.setStatus(AfterSalesStatusEnum.PROCESSED.getValue());
		updateById(afterSales);

		if (AfterSalesTypeEnum.REFUND.getValue().equals(afterSales.getType())) {
			if (YesOrNoEnum.YES.getValue().equals(afterSales.getRefundState())) {
				throw new ServiceException("该售后订单已退款");
			}
			Order order = orderService.getById(afterSales.getOrderId());
			Assert.notNull(order, "订单数据不存在");
			orderService.commonRefund(order, afterSales.getId());
		} else if (AfterSalesTypeEnum.PART_REFUND.getValue().equals(afterSales.getType()) ||
				AfterSalesTypeEnum.NOT_ARRIVE_REFUND.getValue().equals(afterSales.getType())) {
			if (YesOrNoEnum.YES.getValue().equals(afterSales.getRefundState())) {
				throw new ServiceException("该售后订单已退款");
			}
			Order order = orderService.getById(afterSales.getOrderId());
			Assert.notNull(order, "订单数据不存在");
			// 计算退款金额
			List<OrderGoods> goods = afterSalesGoodsService.getGoodsByAfterSalesId(afterSales.getId());
			BigDecimal refundAmount = new BigDecimal(0);
			for (OrderGoods good : goods) {
				refundAmount = refundAmount.add(good.getGoodsPrice().multiply(BigDecimal.valueOf(good.getRefundGoodsNum())));
			}
			orderService.commonRefund(order.getOrderNo(), order.getId(), refundAmount, order.getTotalAmount(), afterSales.getId());
		}
	}

	@Override
	public void refuse(String afterSalesNo, String refuseReason) {
		AfterSales afterSales = lambdaQuery().eq(AfterSales::getAfterSalesNo, afterSalesNo).one();
		Assert.notNull(afterSales, "售后数据不存在");
		if (AfterSalesStatusEnum.REFUSE.getValue().equals(afterSales.getStatus())) {
			throw new ServiceException("已驳回，请勿重复操作");
		}
		if (!AfterSalesStatusEnum.WAIT.getValue().equals(afterSales.getStatus())) {
			throw new ServiceException("当前状态不能驳回");
		}
		afterSales.setStatus(AfterSalesStatusEnum.REFUSE.getValue());
		afterSales.setRefuseReason(refuseReason);
		updateById(afterSales);
	}

	private AfterSales init() {
		AfterSales afterSales = new AfterSales();
		afterSales.setAfterSalesNo("SH" + CommonUtil.genTimeID());
		afterSales.setRefundState(YesOrNoEnum.NO.getValue());
		afterSales.setStatus(AfterSalesStatusEnum.WAIT.getValue());
		return afterSales;
	}

	@Override
	public List<AfterSalesVO> getByGrouponIds(List<Long> longs) {
		return this.baseMapper.getByGrouponIds(longs);
	}


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

		// 判断是否已申请过售后
		Integer count = this.lambdaQuery().eq(AfterSales::getOrderId, order.getId()).eq(AfterSales::getType, AfterSalesTypeEnum.NOT_ARRIVE_REFUND.getValue()).count();
		if (count > 0) {
			throw new ServiceException("请勿重复申请");
		}

		// 查询没有到货的商品
		OrderGroupon orderGroupon = orderGrouponService.lambdaQuery().eq(OrderGroupon::getOrderId, order.getId()).one();
		List<OrderGoods> list = orderGoodsService.lambdaQuery().eq(OrderGoods::getOrderId, order.getId()).list();
		list = list.stream().filter(e -> e.getGoodsNum() - e.getRefundGoodsNumTotal() > 0).collect(Collectors.toList());
		Assert.notEmpty(list, "没有可退的商品");
		List<Long> goodsIds = list.stream().map(OrderGoods::getGoodsId).collect(Collectors.toList());
		List<AppointGoodsVO> notArriveGoods = appointGoodsService.getNotArrive(orderGroupon.getGrouponId(), orderGroupon.getSelfTakeId(), goodsIds);
		if (notArriveGoods.size() == 0) {
			throw new ServiceException("没有未到货的商品");
		}
		Map<Long, Long> map = notArriveGoods.stream().collect(Collectors.toMap(AppointGoods::getGoodsId, AppointGoods::getGoodsId));

		// 生成售后数据
		AfterSales afterSales = init();
		afterSales.setOrderId(order.getId());
		afterSales.setType(AfterSalesTypeEnum.NOT_ARRIVE_REFUND.getValue());
		afterSales.setIsArrived(YesOrNoEnum.NO.getValue());
		afterSales.setUserId(AuthUtil.getUserId());
		afterSales.setGrouponId(orderGroupon.getGrouponId());
		afterSales.setRefuseReason("未到货商品退款");
		this.save(afterSales);

		List<AfterSalesGoods> salesGoodsList = new ArrayList<>();
		for (OrderGoods e : list) {
			int canRefundNum =  e.getGoodsNum() - e.getRefundGoodsNumTotal();
			if (map.get(e.getGoodsId()) != null && canRefundNum > 0) {
				AfterSalesGoods afterSalesGoods = new AfterSalesGoods();
				afterSalesGoods.setAfterSalesId(afterSales.getId());
				afterSalesGoods.setOrderGrouponGoodsId(e.getId());
				afterSalesGoods.setGoodsNum(canRefundNum);
				salesGoodsList.add(afterSalesGoods);
				e.setIsAfterSales(YesOrNoEnum.YES.getValue());
				orderGoodsService.updateById(e);
			}
		}
		Assert.notEmpty(salesGoodsList, "售后商品不能为空");
		afterSalesGoodsService.saveBatch(salesGoodsList);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void refundSubmit(AfterSalesRequest request) {
		Order order = orderService.getById(request.getOrderId());
		Assert.notNull(order, "订单数据不存在");

		order.setIsAftersales(YesOrNoEnum.YES.getValue());
		orderService.updateById(order);
		OrderGroupon orderGroupon = orderGrouponService.lambdaQuery().eq(OrderGroupon::getOrderId, request.getOrderId()).one();
		Assert.notNull(orderGroupon, "orderGroupon数据不存在");
		Groupon groupon = grouponService.getById(orderGroupon.getGrouponId());
		Assert.notNull(orderGroupon, "groupon数据不存在");
		if (groupon.getGrouponEndTime().isBefore(LocalDateTime.now())) {
			throw new ServiceException("团购已结束");
		}

		AfterSales afterSales = init();
		afterSales.setOrderId(request.getOrderId());
		afterSales.setReason("部分退款");
		afterSales.setType(request.getAfterSalesType());
		afterSales.setIsArrived(YesOrNoEnum.NO.getValue());
		afterSales.setUserId(AuthUtil.getUserId());
		afterSales.setGrouponId(orderGroupon.getGrouponId());
		this.save(afterSales);


		Map<Long, OrderGoods> map = request.getAfterGoodsList().stream().collect(Collectors.toMap(BaseEntity::getId, Function.identity()));
		// 校验商品是否已经申请过售后
		List<Long> collect = request.getAfterGoodsList().stream().map(BaseEntity::getId).collect(Collectors.toList());
		List<OrderGoods> list = orderGoodsService.lambdaQuery().in(BaseEntity::getId, collect).list();
		for (OrderGoods orderGoods : list) {
			if (YesOrNoEnum.YES.getValue().equals(orderGoods.getIsAfterSales())) {
				throw new ServiceException("商品【"+orderGoods.getGoodsName()+"】已申请过退款");
			}
			OrderGoods grouponGoods = map.get(orderGoods.getId());
			orderGoods.setAfterSalesGoodsNum(orderGoods.getAfterSalesGoodsNum() + grouponGoods.getGoodsNum());
			orderGoods.setRefundGoodsNumTotal(orderGoods.getRefundGoodsNumTotal() + grouponGoods.getGoodsNum());
			orderGoods.setIsAfterSales(YesOrNoEnum.YES.getValue());
		}
		// 更新售后状态
		orderGoodsService.updateBatchById(list);

		List<AfterSalesGoods> salesGoodsList = new ArrayList<>();
		for (OrderGoods orderGoods : request.getAfterGoodsList()) {
			AfterSalesGoods afterSalesGoods = new AfterSalesGoods();
			afterSalesGoods.setAfterSalesId(afterSales.getId());
			afterSalesGoods.setOrderGrouponGoodsId(orderGoods.getId());
			afterSalesGoods.setGoodsNum(orderGoods.getGoodsNum());
			salesGoodsList.add(afterSalesGoods);
		}
		afterSalesGoodsService.saveBatch(salesGoodsList);

		// 查询改订单下的商品是否都已申请退款，如果是则吧状态更新为退款中
		Integer count = orderGoodsService.lambdaQuery().eq(OrderGoods::getOrderId, order.getId())
				.eq(OrderGoods::getIsAfterSales, YesOrNoEnum.NO.getValue()).count();
		// 判断所有的商品是否都已申请退款
		boolean flag = true;
		for (OrderGoods goods : list) {
			if (goods.getGoodsNum() > goods.getAfterSalesGoodsNum()) {
				flag = false;
				break;
			}
		}
		if (count < 1 && flag) {
			order.setOrderState(OrderStateEnum.REFUNDING.getValue());
			orderService.updateById(order);
		}

	}

	@Override
	public void confirm(Long orderId, Long afterSalesId) {
		AfterSales afterSales = this.getById(afterSalesId);
		Assert.notNull(afterSales, "售后数据不存在");

		if (YesOrNoEnum.YES.getValue().equals(afterSales.getIsArrived())) {
			throw new ServiceException("已确认收货，请勿重复操作");
		}

		if (YesOrNoEnum.YES.getValue().equals(afterSales.getRefundState())) {
			throw new ServiceException("该售后订单已退款");
		}

		Order order = orderService.getById(orderId);
		Assert.notNull(order, "订单数据不存在");

		OrderGoods orderGoods = orderGoodsService.getById(afterSales.getOrderGoodsId());
		Assert.notNull(order, "orderGrouponGoods数据不存在");

		OrderRefund orderRefund = orderRefundService.lambdaQuery().eq(OrderRefund::getAfterSalesId, afterSalesId).eq(OrderRefund::getRefundState, RefundStateEnum.ONLY_REFUND.getValue()).one();
		if (orderRefund != null) {
			throw new ServiceException("申请中，请勿重复操作");
		}

		BigDecimal refundPrice = orderGoods.getGoodsPrice().multiply(BigDecimal.valueOf(orderGoods.getGoodsNum()));
		orderService.commonRefund(order.getOrderNo(), order.getId(), refundPrice, order.getTotalAmount(), afterSalesId);

		afterSales.setIsArrived(YesOrNoEnum.YES.getValue());
		this.updateById(afterSales);
	}

	@Override
	public void finish(AfterSales afterSales) {
		afterSales.setRefundState(YesOrNoEnum.YES.getValue());
		afterSales.setStatus(AfterSalesStatusEnum.FINISH.getValue());
		updateById(afterSales);

		// 类型为售后的时候，查询该订单下是否还有未处理的售后，如果没有则重新计算收益
		if (AfterSalesTypeEnum.PART_REFUND.getValue().equals(afterSales.getType())) {
			List<AfterSales> list = lambdaQuery().eq(AfterSales::getOrderId, afterSales.getOrderId()).list();
			if (CollUtil.isNotEmpty(list)) {
				List<AfterSales> collect = list.stream().filter(e -> AfterSalesStatusEnum.PROCESSED.getValue().equals(e.getStatus())).collect(Collectors.toList());
				// 如果没有则重新计算收益
				if (CollUtil.isEmpty(collect)) {
					List<Long> longs = list.stream().map(AfterSales::getId).collect(Collectors.toList());
					List<OrderRefund> refunds = orderRefundService.lambdaQuery().in(OrderRefund::getAfterSalesId, longs).list();

					BigDecimal totalRefund = new BigDecimal(0);
					// 计算总的退款金额
					for (OrderRefund refund : refunds) {
						totalRefund = totalRefund.add(refund.getRefuseAmount());
					}
					Order order = orderService.getById(afterSales.getOrderId());
					// 退款金额跟订单金额一致则不处理
					if (!order.getPaymentAmount().equals(totalRefund)) {
						commissionService.commissionCalculationTwo(order.getId(), order.getPaymentAmount().subtract(totalRefund));
					}
				}
			}
		} else if (AfterSalesTypeEnum.PART_REFUND.getValue().equals(afterSales.getType())) {
			List<OrderGoods> goods = afterSalesGoodsService.getGoodsByAfterSalesId(afterSales.getId());
			for (OrderGoods good : goods) {
				good.setIsRefund(YesOrNoEnum.YES.getValue());
			}
			orderGoodsService.updateBatchById(goods);

			// 如果该订单下所有商品都已退款，则吧订单状态修改为已退款
			Integer count = orderGoodsService.lambdaQuery().eq(OrderGoods::getOrderId, afterSales.getOrderId()).eq(OrderGoods::getIsRefund, YesOrNoEnum.NO.getValue())
					.count();

			// 判断所有的商品是否都已申请退款
			boolean flag = true;
			for (OrderGoods g : goods) {
				if (g.getGoodsNum() > g.getAfterSalesGoodsNum()) {
					flag = false;
					break;
				}
			}
			if (count < 1 && flag) {
				orderService.lambdaUpdate().set(Order::getOrderState, OrderStateEnum.REFUNDED.getValue()).eq(BaseEntity::getId, afterSales.getOrderId()).update();
			}
		}
	}

	@Override
	public List<AfterSalesVO> getByGrouponId(Long grouponId, Long selfTakeId) {
		return this.baseMapper.getByGrouponId(grouponId, selfTakeId);
	}

	@Override
	public AfterSalesVO getUserAfterSalesDetail(Long afterSalesId) {
		AfterSales afterSales = getById(afterSalesId);
		AfterSalesVO vo = AfterSalesWrapper.build().entityVO(afterSales);
		if (AfterSalesTypeEnum.REFUND.getValue().equals(vo.getType())) {
			List<OrderGoods> list = orderGoodsService.lambdaQuery().eq(OrderGoods::getOrderId, vo.getOrderId()).list();
			vo.setGoodsList(list);
		} else {
			List<OrderGoods> list = orderGoodsService.lambdaQuery().eq(BaseEntity::getId, vo.getOrderGoodsId()).list();
			vo.setGoodsList(list);
		}
		Order order = orderService.getById(afterSales.getOrderId());
		vo.setOrder(OrderWrapper.build().entityVO(order));

		if (YesOrNoEnum.YES.getValue().equals(vo.getRefundState())) {
			OrderRefund refund = orderRefundService.lambdaQuery().eq(OrderRefund::getAfterSalesId, vo.getId()).one();
			vo.setRefund(refund);
		}

//		if (!AfterSalesStatusEnum.WAIT.getValue().equals(afterSales.getStatus()) || !AfterSalesStatusEnum.REFUSE.getValue().equals(afterSales.getStatus())) {
//			OrderGoods orderGoods = orderGrouponGoodsService.getById(afterSales.getOrderGrouponGoodsId());
//			Store store = storeService.getById(orderGoods.getSupplierId());
//			vo.setStore(StoreWrapper.build().entityVO(store));
//		}

		return vo;
	}

	@Override
	public IPage<AfterSalesVO> getUserAfterSalesList(IPage page, Long userId, Integer state) {
		List<AfterSalesVO> afterSalesVOS = baseMapper.selectUserAfterSalesList(page, userId, state);
		if (CollUtil.isNotEmpty(afterSalesVOS)) {
			for (AfterSalesVO vo : afterSalesVOS) {
				if (AfterSalesTypeEnum.REFUND.getValue().equals(vo.getType())) {
					List<OrderGoods> list = orderGoodsService.lambdaQuery().eq(OrderGoods::getOrderId, vo.getOrderId()).list();
					vo.setGoodsList(list);
				} else {
					List<OrderGoods> list = orderGoodsService.lambdaQuery().eq(BaseEntity::getId, vo.getOrderGoodsId()).list();
					vo.setGoodsList(list);
				}
			}
		}
		return page.setRecords(afterSalesVOS);
	}

	@Override
	public void bindLogisticsNo(String afterSalesNo, String logisticsNo) {
		AfterSales afterSales = lambdaQuery().eq(AfterSales::getAfterSalesNo, afterSalesNo).one();
		Assert.notNull(afterSales, "售后数据不存在");
		afterSales.setLogisticsNo(logisticsNo);
		this.updateById(afterSales);
	}
}
