
package com.tmerclub.cloud.delivery.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.tmerclub.cloud.api.delivery.bo.BmapPointBO;
import com.tmerclub.cloud.api.delivery.constant.DistanceUtil;
import com.tmerclub.cloud.api.delivery.constant.GraphUtils;
import com.tmerclub.cloud.api.delivery.constant.SameCityChargeType;
import com.tmerclub.cloud.common.cache.constant.DeliveryCacheNames;
import com.tmerclub.cloud.common.order.vo.ShopCartItemVO;
import com.tmerclub.cloud.common.order.vo.ShopTransFeeVO;
import com.tmerclub.cloud.common.order.vo.UserAddrVO;
import com.tmerclub.cloud.common.order.vo.UserDeliveryInfoVO;
import com.tmerclub.cloud.common.product.constant.PreSaleType;
import com.tmerclub.cloud.common.util.Arith;
import com.tmerclub.cloud.common.util.Json;
import com.tmerclub.cloud.delivery.mapper.SameCityMapper;
import com.tmerclub.cloud.delivery.model.SameCity;
import com.tmerclub.cloud.delivery.service.SameCityService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 同城配送信息
 *
 * @author tmerclub
 * @date 2020-12-07 15:10:01
 */
@Service
public class SameCityServiceImpl implements SameCityService {

    private final Logger LOGGER = LoggerFactory.getLogger(SameCityServiceImpl.class);

    @Autowired
    private SameCityMapper sameCityMapper;

    @Override
    public void save(SameCity sameCity) {
        sameCityMapper.save(sameCity);
    }

    @Override
    @CacheEvict(cacheNames = DeliveryCacheNames.SAME_CITY_BY_ID_PREFIX, key = "#sameCity.shopId")
    public void updateByShopId(SameCity sameCity) {
        sameCityMapper.updateByShopId(sameCity);
    }

    @Override
    public long calculateTransFee(List<ShopCartItemVO> shopCartItems, UserDeliveryInfoVO userDeliveryInfo) {
        UserAddrVO userAddr = userDeliveryInfo.getUserAddr();
        long transFee = 0L;
        if (CollectionUtil.isEmpty(shopCartItems) || Objects.isNull(userAddr)) {
            LOGGER.info("计算同城配送运费，订单项为空或者用户地址为空，直接返回0");
            return transFee;
        }
        Long shopId = shopCartItems.get(0).getShopId();
        transFeePut(shopId, userDeliveryInfo, transFee, shopCartItems);
        SameCity sameCity = getSameCityByShopId(shopId);
        // 如果商家没有配好同城配送或者没有启用的模板，直接返回 -2
        if (Objects.isNull(sameCity) || Objects.equals(sameCity.getStatus(), 0)) {
            LOGGER.info("计算同城配送运费，商家没有配好同城配送或者没有启用的模板，直接返回 -2");
            return -2;
        }
        //如果没有定位，直接返回 -1
        if (Objects.isNull(userAddr.getLat()) || Objects.isNull(userAddr.getLng())) {
            LOGGER.info("计算同城配送运费，用户没有定位，直接返回 -1");
            return -1;
        }
        //计算当前的坐标点
        BmapPointBO bmapPoint = new BmapPointBO(userAddr.getLng(), userAddr.getLat());

        List<BmapPointBO> bmapPoints = Json.parseArray(sameCity.getPositionInfo(), BmapPointBO[].class);

        boolean point = GraphUtils.isPointInPolygon(bmapPoint, bmapPoints);
        //如果不在配送范围，直接返回 -1
        if (!point) {
            LOGGER.info("计算同城配送运费，用户不在配送范围，直接返回 -1");
            return -1;
        }
        // 计算总重、总价
        long productTotalAmount = 0;
        BigDecimal productTotalWeight = new BigDecimal(0);
        for (ShopCartItemVO shopCartItem : shopCartItems) {
            if (shopCartItem.getMold() != null && shopCartItem.getMold() == 1) {
                continue;
            }
            productTotalAmount += shopCartItem.getTotalAmount();
            productTotalWeight = shopCartItem.getWeight().add(productTotalWeight);
            productTotalWeight = productTotalWeight.multiply(new BigDecimal(shopCartItem.getCount()));
        }
        //1.先判断起送费有没有够,没够直接返回 -3
        if (sameCity.getStartFee() > productTotalAmount) {
            LOGGER.info("计算同城配送运费，起送费不够，直接返回 -3");
            return -3;
        }

        //2.判断收费类型 2.1 固定费用先加上
        transFee = sameCity.getDeliveryFee();
        if (Objects.equals(sameCity.getChargeType(), SameCityChargeType.DISTANCE.value())) {
            // 2.2按距离收费,sql查询出来的单位是m
            double distance = DistanceUtil.getDistance(userAddr.getLat(), userAddr.getLng(), sameCity.getLat(), sameCity.getLng());
            distance = Arith.div(distance, 1000, 1);

            LOGGER.info("按距离收费，距离为：{}，免费配送距离为：{}", distance, sameCity.getDefaultDistance());
            if (distance > sameCity.getDefaultDistance()) {
                // 减去免费配送距离
                double prodContinuousPiece = Arith.sub(distance, sameCity.getDefaultDistance());
                // 超出距离的倍数，向上取整
                Integer mulNumber = (int) Math.ceil(Arith.div(prodContinuousPiece, sameCity.getOverDistance()));
                // 超出距离的运费
                long continuousFee = mulNumber * sameCity.getOverDistanceFee();
                transFee += continuousFee;
                LOGGER.info("按距离收费，超出距离的运费为：{}", continuousFee);
            }
        }
        //3.按重量计算运费
        double weight = productTotalWeight.doubleValue();
        if (weight > sameCity.getFreeWeight() && Objects.nonNull(sameCity.getOverWeight()) && sameCity.getOverWeight() > 0.0) {
            // 续重重量
            double prodContinuousWeight = Arith.sub(weight, sameCity.getFreeWeight());
            // 续重重量的倍数，向上取整
            int mulNumber = (int) Math.ceil(Arith.div(prodContinuousWeight, sameCity.getOverWeight()));
            // 续重数量运费
            long continuousFee = mulNumber * sameCity.getOverWeightFee();
            transFee += continuousFee;
            LOGGER.info("按重量计算运费，续重数量运费为：{}", continuousFee);
        }
        transFeePut(shopId, userDeliveryInfo, transFee, shopCartItems);
        return transFee;
    }

    private void transFeePut(long shopId, UserDeliveryInfoVO userDeliveryInfo, long transFee, List<ShopCartItemVO> shopCartItems) {
        Map<Long, ShopTransFeeVO> shopIdWithShopTransFee = new HashMap<>(16);
        Map<Long, ShopTransFeeVO> supplierIdWithShopTransFeeMap = new HashMap<>(16);
        Map<Long, ShopTransFeeVO> preSaleTransFeeMap = new HashMap<>(16);

        ShopTransFeeVO shopTransFeeVO = new ShopTransFeeVO();
        shopTransFeeVO.setTransfee(transFee);
        shopTransFeeVO.setFreeTransfee(0L);
        shopIdWithShopTransFee.put(shopId, shopTransFeeVO);

        for (ShopCartItemVO shopCartItemVO : shopCartItems) {
            //如果是预售需要处理运费
            if (!Objects.equals(shopCartItemVO.getPreSaleType(), PreSaleType.DISABLE.value())) {
                LOGGER.info("计算预售商品运费，shopCartItemVO：{}", shopCartItemVO);
                long deliveryTemplateId = -(shopCartItemVO.getDeliveryTemplateId() + shopCartItemVO.getSkuId());
                if (deliveryTemplateId < 0) {
                    shopTransFeeVO = new ShopTransFeeVO();
                    shopTransFeeVO.setTransfee(transFee);
                    shopTransFeeVO.setDeliveryTemplateId(shopCartItemVO.getDeliveryTemplateId());
                    // 没有减免运费的计算
                    shopTransFeeVO.setFreeTransfee(0L);
                    preSaleTransFeeMap.put(shopCartItemVO.getSkuId(), shopTransFeeVO);
                    LOGGER.info("计算预售商品运费，shopCartItemVO：{}", shopCartItemVO);
                }
            }
            // 开始计算供应商的运费
            Long supplierId = shopCartItemVO.getSupplierId() == null ? 0 : shopCartItemVO.getSupplierId();
            // 如果为0就不需要计算供应商的运费
            if (Objects.equals(supplierId, 0L)) {
                LOGGER.info("计算供应商的运费，supplierId为0，不需要计算供应商的运费，shopCartItemVO：{}", shopCartItemVO);
                continue;
            }
            ShopTransFeeVO supplierIdWithShopTransFee = supplierIdWithShopTransFeeMap.get(supplierId);
            // 如果商家没有计算运费就初始化运费
            if (Objects.isNull(supplierIdWithShopTransFee)) {
                supplierIdWithShopTransFee = new ShopTransFeeVO();
                supplierIdWithShopTransFee.setTransfee(transFee);
                supplierIdWithShopTransFee.setSupplierId(supplierId);
                supplierIdWithShopTransFee.setDeliveryTemplateId(shopCartItemVO.getDeliveryTemplateId());
                // 没有减免运费的计算
                supplierIdWithShopTransFee.setFreeTransfee(0L);
                supplierIdWithShopTransFeeMap.put(supplierId, supplierIdWithShopTransFee);
                LOGGER.info("商家没有计算运费就初始化运费,shopCartItemVO：{}", shopCartItemVO);
            }
            // 累加非同一个运费模板的运费
            else if (!Objects.equals(supplierIdWithShopTransFee.getDeliveryTemplateId(), shopCartItemVO.getDeliveryTemplateId())) {
                supplierIdWithShopTransFee.setTransfee(transFee + supplierIdWithShopTransFee.getTransfee());
                supplierIdWithShopTransFee.setSupplierId(supplierId);
                LOGGER.info("累加非同一个运费模板的运费，shopCartItemVO：{}", shopCartItemVO);
            }
        }
        Map<Long, ShopTransFeeVO> oldShopTransFee = userDeliveryInfo.getShopIdWithShopTransFee();
        if (Objects.nonNull(oldShopTransFee)) {
            oldShopTransFee.putAll(shopIdWithShopTransFee);
        } else {
            userDeliveryInfo.setShopIdWithShopTransFee(shopIdWithShopTransFee);
        }
        Map<Long, ShopTransFeeVO> oldPreSaleTransFeeMap = userDeliveryInfo.getPreSaleTransFeeMap();
        if (Objects.nonNull(oldPreSaleTransFeeMap)) {
            oldPreSaleTransFeeMap.putAll(preSaleTransFeeMap);
        } else {
            userDeliveryInfo.setPreSaleTransFeeMap(preSaleTransFeeMap);
        }
        if (Objects.isNull(userDeliveryInfo.getSupplierIdWithShopTransFeeMap())) {
            userDeliveryInfo.setSupplierIdWithShopTransFeeMap(new HashMap<>(supplierIdWithShopTransFeeMap.size()));
        }
        userDeliveryInfo.getSupplierIdWithShopTransFeeMap().put(shopId, supplierIdWithShopTransFeeMap);

    }

    @Override
    @Cacheable(cacheNames = DeliveryCacheNames.SAME_CITY_BY_ID_PREFIX, key = "#shopId")
    public SameCity getSameCityByShopId(Long shopId) {
        return sameCityMapper.getByShopId(shopId);
    }

    @Override
    @CacheEvict(cacheNames = DeliveryCacheNames.SAME_CITY_BY_ID_PREFIX, key = "#shopId")
    public void removeSameCityCacheByShopId(Long shopId) {

    }


}
