package com.freemud.sdk.api.assortment.shoppingcart.service.impl;

import ch.qos.logback.classic.Level;
import cn.freemud.base.entity.BaseResponse;
import cn.freemud.redis.RedisCache;
import com.freemud.application.sdk.api.base.SDKCommonBaseContextWare;
import com.freemud.application.sdk.api.couponcenter.online.service.FMActiveSdkService;
import com.freemud.application.sdk.api.log.ErrorLog;
import com.freemud.application.sdk.api.log.LogThreadLocal;
import com.freemud.sdk.api.assortment.shoppingcart.constant.CartResponseConstant;
import com.freemud.sdk.api.assortment.shoppingcart.constant.CommonsConstant;
import com.freemud.sdk.api.assortment.shoppingcart.constant.MealClearOperationEnum;
import com.freemud.sdk.api.assortment.shoppingcart.constant.RedisKeyConstant;
import com.freemud.sdk.api.assortment.shoppingcart.domain.CartAddItem;
import com.freemud.sdk.api.assortment.shoppingcart.domain.CartGoods;
import com.freemud.sdk.api.assortment.shoppingcart.domain.CartParamDto;
import com.freemud.sdk.api.assortment.shoppingcart.exception.ServiceException;
import com.freemud.sdk.api.assortment.shoppingcart.request.CheckCartRequest;
import com.freemud.sdk.api.assortment.shoppingcart.service.ShoppingCartBaseService;
import com.freemud.sdk.api.assortment.shoppingcart.util.CartResponseUtil;
import com.freemud.sdk.api.assortment.shoppingcart.util.ShoppingSdkLogUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * All rights Reserved, Designed By www.freemud.com
 *
 * @version V1.0
 * @Title:
 * @Package: com.freemud.sdk.api.assortment.shoppingcart.service.impl
 * @Descripttion:
 * @author: cuigenyou
 * @date: 2019/9/2 10:05
 * @Copyright: 2017 www.freemud.cn Inc. All rights reserved.
 * 注意：本内容仅限于上海非码科技内部传阅，禁止外泄以及用于其他的商业目.
 */
@Service("mealCartService")
public class MealCartBaseServiceImpl implements ShoppingCartBaseService {

    @Value("${coupon.app.id}")
    private String appId;
    @Autowired
    private FMActiveSdkService fmActiveSdkService;

    @Autowired
    private RedisCache redisCache;

    /**
     * 购物车人员缓存
     *
     * @param partnerId
     * @param storeId
     * @param tableNumber
     * @return
     */
    private String genMealMemberHashKey(String partnerId, String storeId, String tableNumber) {
        return MessageFormat.format(RedisKeyConstant.MEAL_MEMBER_HASH_KEY, partnerId, storeId, tableNumber);
    }

    /**
     * 购物车版本号缓存
     *
     * @param partnerId
     * @param storeId
     * @param tableNumber
     * @return
     */
    private String genMealCartVerKey(String partnerId, String storeId, String tableNumber) {
        return MessageFormat.format(RedisKeyConstant.MEAL_CART_VER_KEY, partnerId, storeId, tableNumber);
    }

    /**
     * 购物车商品缓存
     *
     * @param partnerId
     * @param storeId
     * @param tableNumber
     * @return
     */
    private String genMealCartBaseHashKey(String partnerId, String storeId, String tableNumber) {
        return MessageFormat.format(RedisKeyConstant.MEAL_CART_BASE_HASH_KEY, partnerId, storeId, tableNumber);
    }

    /**
     * 锁定购物车行
     *
     * @param partnerId
     * @param storeId
     * @param tableNumber
     * @return
     */
    private String genMealLockKey(String partnerId, String storeId, String tableNumber) {
        return MessageFormat.format(RedisKeyConstant.MEAL_CART_LOCK_KEY, partnerId, storeId, tableNumber);
    }

    /**
     * 增加购物车版本
     *
     * @param partnerId   商户号
     * @param storeId     门店号
     * @param tableNumber 桌号
     * @return
     */
    public Integer incrementCartVersion(String partnerId, String storeId, String tableNumber) {
        return redisCache.opsForValue().increment(genMealCartVerKey(partnerId, storeId, tableNumber), 1).intValue();
    }

    /**
     * 获取当前购物车版本
     *
     * @param partnerId   商户号
     * @param storeId     门店号
     * @param tableNumber 桌号
     * @return
     */
    public Integer getCartVersion(String partnerId, String storeId, String tableNumber) {
        Integer value = redisCache.getValue(genMealCartVerKey(partnerId, storeId, tableNumber));
        return value == null ? 0 : value;
    }

    /**
     * 锁定购物车行
     *
     * @param partnerId
     * @param storeId
     * @param tableNumber
     * @param cartIdList
     */
    public void lockCart(String partnerId, String storeId, String tableNumber, List<String> cartIdList) {
        //todo 锁定一分钟，如果去下单1分钟后没有调用清空clear，则认为下单失败，恢复购物车
        redisCache.save(genMealLockKey(partnerId, storeId, tableNumber), cartIdList, redisCache.getExpireConfig().getExpire(), TimeUnit.MINUTES);
    }

    /**
     * 获取锁定的购物车行
     *
     * @param partnerId
     * @param storeId
     * @param tableNumber
     * @return
     */
    public List<String> getLockCart(String partnerId, String storeId, String tableNumber) {
        List<String> result = redisCache.getValue(genMealLockKey(partnerId, storeId, tableNumber));
        if (CollectionUtils.isEmpty(result)) {
            result = new ArrayList<>(0);
        }
        return result;
    }

    /**
     * 释放锁定购物车行
     *
     * @param partnerId
     * @param storeId
     * @param tableNumber
     */
    public void clearLockCart(String partnerId, String storeId, String tableNumber) {
        redisCache.delete(genMealLockKey(partnerId, storeId, tableNumber));
    }


    /**
     * 获取点餐用户列表
     *
     * @param partnerId   商户号
     * @param storeId     门店号
     * @param tableNumber 桌号
     * @return
     */
    public List<String> getDinnerUsers(String partnerId, String storeId, String tableNumber) {
        Map<String, Object> baseDataMap = redisCache.hashGetAll(genMealMemberHashKey(partnerId, storeId, tableNumber));
        if (CollectionUtils.isEmpty(baseDataMap)) {
            return new ArrayList<>();
        }
        return new ArrayList<>(baseDataMap.keySet());

    }

    /**
     * 获取点餐用户以及购物车行集合
     *
     * @param partnerId   商户号
     * @param storeId     门店号
     * @param tableNumber 桌号
     * @return 点餐用户以及购物车行集合
     */
    public Map<String, List<String>> getUserCarts(String partnerId, String storeId, String tableNumber) {
        return redisCache.hashGetAll(genMealMemberHashKey(partnerId, storeId, tableNumber));
    }

    /**
     * 根据用户sessionId获取该用户购物车行id集合
     *
     * @param partnerId
     * @param storeId
     * @param tableNumber
     * @param sessionId
     * @return
     */
    public List<String> getUserCartIdList(String partnerId, String storeId, String tableNumber, String sessionId) {
        List<String> cartIds = redisCache.hashGet(genMealMemberHashKey(partnerId, storeId, tableNumber), sessionId);
        if (cartIds == null) {
            return new ArrayList<>();
        }
        return cartIds;
    }

    /**
     * 获取所有购物车行
     *
     * @param partnerId
     * @param storeId
     * @param tableNumber
     * @return
     */
    public Map<String, CartGoods> getAllCartGoods(String partnerId, String storeId, String tableNumber) {
        String mealCartHashKey = genMealCartBaseHashKey(partnerId, storeId, tableNumber);
        return redisCache.hashGetAll(mealCartHashKey);
    }

    public void removeCartGoodId(String partnerId, String storeId, String tableNumber, List<String> cartIdList) {
        if (CollectionUtils.isEmpty(cartIdList)) {
            return;
        }
        String mealCartHashKey = genMealCartBaseHashKey(partnerId, storeId, tableNumber);
        cartIdList.forEach(each -> redisCache.deleteHashKey(mealCartHashKey, each));
    }


    /**
     * 添加用户购物车行记录
     *
     * @param sessionId   会话id
     * @param cartGoodId  购物车行
     * @param partnerId   商户号
     * @param storeId     门店号
     * @param tableNumber 桌号
     */
    public void addUserCarts(String sessionId, String cartGoodId, String partnerId, String storeId, String tableNumber) {
        String mealBaseHashKey = genMealMemberHashKey(partnerId, storeId, tableNumber);
        List<String> cartGoodIdList = redisCache.hashGet(mealBaseHashKey, sessionId);

        if (CollectionUtils.isEmpty(cartGoodIdList)) {
            cartGoodIdList = new ArrayList<>();
        }
        cartGoodIdList.add(cartGoodId);
        redisCache.hashPut(mealBaseHashKey, sessionId, cartGoodIdList);
    }

    /**
     * 移除用户购物车行记录
     *
     * @param sessionId   会话id
     * @param cartGoodIds 购物车行id集合
     * @param partnerId   商户号
     * @param storeId     门店号
     * @param tableNumber 桌号
     */
    public void removeUserCart(String sessionId, List<String> cartGoodIds, String partnerId, String storeId, String tableNumber) {
        String mealBaseHashKey = genMealMemberHashKey(partnerId, storeId, tableNumber);
        List<String> cartGoodIdList = redisCache.hashGet(mealBaseHashKey, sessionId);

        if (CollectionUtils.isEmpty(cartGoodIdList)) {
            return;
        }
        if (CollectionUtils.isEmpty(cartGoodIds)) {
            redisCache.deleteHashKey(mealBaseHashKey, sessionId);
            return;
        }
        cartGoodIdList.removeAll(cartGoodIds);
        redisCache.hashPut(mealBaseHashKey, sessionId, cartGoodIdList);
    }

    @Override
    public boolean addCartGoodList(CartParamDto cartParamDto) {
        String sessionId = cartParamDto.getSessionId();
        List<CartGoods> cartGoodsList = cartParamDto.getCartGoodsList();
        if (CollectionUtils.isEmpty(cartGoodsList)) {
            return true;
        }
        try {
            CartGoods cartGoods = cartGoodsList.get(0);
            addUserCarts(sessionId, cartGoods.getCartGoodsUid(), cartParamDto.getPartnerId(), cartParamDto.getStoreId(), cartParamDto.getTableNumber());
            String hashKey = genMealCartBaseHashKey(cartParamDto.getPartnerId(), cartParamDto.getStoreId(), cartParamDto.getTableNumber());
            redisCache.hashPut(hashKey, cartGoods.getCartGoodsUid(), cartGoods);
            this.incrementCartVersion(cartParamDto.getPartnerId(), cartParamDto.getStoreId(), cartParamDto.getTableNumber());
        } catch (Exception e) {
            ShoppingSdkLogUtil.printLog(SDKCommonBaseContextWare.getAppName(), LogThreadLocal.getTrackingNo(), getClass(), "添加购物车失败:" + e.getMessage(), e, Level.ERROR);
            return false;
        }
        return true;
    }

    @Override
    public BaseResponse<List<CartAddItem>> detailCart(CartParamDto cartParamDto, String trackingNo) {
        return null;
    }

    @Override
    public BaseResponse<List<CartGoods>> getCartGoodsList(CartParamDto cartParamDto, String trackingNo) {
        doCheck(cartParamDto);
        Map<String, CartGoods> allCartGoods = getAllCartGoods(cartParamDto.getPartnerId(), cartParamDto.getStoreId(), cartParamDto.getTableNumber());
        List<CartGoods> result = allCartGoods.values().stream().sorted(Comparator.comparingLong(CartGoods::getCreateTimeMili)).collect(Collectors.toList());
        List<String> lockCart = getLockCart(cartParamDto.getPartnerId(), cartParamDto.getStoreId(), cartParamDto.getTableNumber());
        if (StringUtils.isEmpty(cartParamDto.getSessionId())) {
            //过滤掉锁定的购物车行
            result.removeIf(each -> lockCart.contains(each.getCartGoodsUid()));
            return CartResponseUtil.success(result);
        }
        List<String> userCartIdList = getUserCartIdList(cartParamDto.getPartnerId(), cartParamDto.getStoreId(), cartParamDto.getTableNumber(), cartParamDto.getSessionId());
        //筛选出当前人的购物车行并移除掉已经锁定的
        result = allCartGoods.entrySet().stream().filter(each ->
                userCartIdList.contains(each.getKey()) && !lockCart.contains(each.getKey())).map(Map.Entry::getValue).collect(Collectors.toList());
        return CartResponseUtil.success(result);
    }

    private void doCheck(CartParamDto cartParamDto) {
        if (StringUtils.isEmpty(cartParamDto.getStoreId()) || StringUtils.isEmpty(cartParamDto.getTableNumber())) {
            //门店和桌号不能为空
            throw new ServiceException(CartResponseConstant.FAIL, "门店号和桌号不能为空");
        }
    }

    @Override
    public BaseResponse<List<CartGoods>> setCartGoodsList(CartParamDto cartParamDto, String trackingNo) {
        return null;
    }

    @Override
    public BaseResponse clear(CartParamDto cartParamDto, String trackingNo) {
        MealClearOperationEnum operationType = cartParamDto.getOperationType();
        if (operationType == null) {
            return new BaseResponse(CartResponseConstant.FAIL.getCode(), "operationType不能为空");
        }
        try {
            this.incrementCartVersion(cartParamDto.getPartnerId(), cartParamDto.getStoreId(), cartParamDto.getTableNumber());
            Method method = this.getClass().getMethod(operationType.getMethod(), CartParamDto.class);
            method.invoke(this, cartParamDto);
        } catch (Exception e) {
            ShoppingSdkLogUtil.printLog(SDKCommonBaseContextWare.getAppName(), trackingNo, getClass(), e.getMessage(), e, Level.ERROR);
            e.printStackTrace();
            return CartResponseUtil.error(e.getMessage());
        }
        return CartResponseUtil.success();
    }


    /**
     * 下单成功，清除锁定行
     * clear 方法根据 operationType 反射方法执行
     *
     * @param cartParamDto
     */
    public void clearLock(CartParamDto cartParamDto) {
        String partnerId = cartParamDto.getPartnerId();
        String storeId = cartParamDto.getStoreId();
        String tableNumber = cartParamDto.getTableNumber();
        List<String> lockCartList = this.getLockCart(partnerId, storeId, tableNumber);
        clearLockCart(partnerId, storeId, tableNumber);
        removeCartGoodId(partnerId, storeId, tableNumber, lockCartList);
    }

    /**
     * 下单失败，恢复锁定行
     * clear 方法根据 operationType 反射方法执行
     *
     * @param cartParamDto
     */
    public void recoverLock(CartParamDto cartParamDto) {
        String partnerId = cartParamDto.getPartnerId();
        String storeId = cartParamDto.getStoreId();
        String tableNumber = cartParamDto.getTableNumber();
        clearLockCart(partnerId, storeId, tableNumber);
    }

    /**
     * 清除所有
     * clear 方法根据 operationType 反射方法执行
     *
     * @param cartParamDto
     */
    public void clearAll(CartParamDto cartParamDto) {
        String partnerId = cartParamDto.getPartnerId();
        String storeId = cartParamDto.getStoreId();
        String tableNumber = cartParamDto.getTableNumber();
        //清除锁定行
        clearLockCart(partnerId, storeId, tableNumber);
        //清除购物车
        redisCache.delete(genMealCartBaseHashKey(partnerId, storeId, tableNumber));
        //清除购物车版本
        redisCache.delete(genMealCartVerKey(partnerId, storeId, tableNumber));
        //清除人员
        redisCache.delete(genMealMemberHashKey(partnerId, storeId, tableNumber));
    }


    @Override
    public String getCouponAppId() {
        return this.appId;
    }

    @Override
    public Map<String, Boolean> getCouponOrderWay(String partnerId, List<String> activityCodes, Integer orderTye, String trackingNo) {
        return null;
    }


    @Override
    public BaseResponse<CartGoods> getCartGoods(CartParamDto cartParamDto, String trackingNo) {
        String partnerId = cartParamDto.getPartnerId();
        String storeId = cartParamDto.getStoreId();
        String tableNumber = cartParamDto.getTableNumber();
        List<String> lockCart = this.getLockCart(partnerId, storeId, tableNumber);
        if (!CollectionUtils.isEmpty(lockCart) && lockCart.contains(cartParamDto.getCartGoodsUid())) {
            //该行已经被锁定
            return CartResponseUtil.error("该行已经被下单，请重新添加");
        }
        String mealCartBaseHashKey = genMealCartBaseHashKey(partnerId, storeId, tableNumber);
        CartGoods cartGoods = redisCache.hashGet(mealCartBaseHashKey, cartParamDto.getCartGoodsUid());

        return CartResponseUtil.success(cartGoods);
    }

    @Override
    public BaseResponse<List<CartGoods>> updateGoodsQty(CartParamDto cartParamDto, String trackingNo) {
        String mealCartBaseHashKey = genMealCartBaseHashKey(cartParamDto.getPartnerId(), cartParamDto.getStoreId(), cartParamDto.getTableNumber());
        CartGoods cartGoods = redisCache.hashGet(mealCartBaseHashKey, cartParamDto.getCartGoodsUid());
        if (cartGoods == null) {
            return null;
        }
        // 当数量为0时,移除
        if (Objects.equals(cartParamDto.getQty(), 0)) {
            redisCache.deleteHashKey(mealCartBaseHashKey, cartParamDto.getCartGoodsUid());
        } else {
            cartGoods.setQty(cartParamDto.getQty());
            redisCache.hashPut(mealCartBaseHashKey, cartParamDto.getCartGoodsUid(), cartGoods);
        }
        this.incrementCartVersion(cartParamDto.getPartnerId(), cartParamDto.getStoreId(), cartParamDto.getTableNumber());
        return null;
    }

    @Override
    public void checkNoProductExistMenu(CheckCartRequest checkCartRequest, Set<String> keySet) {
        // 当商品不存在于菜单中且不是商品券时，需置空移除
        List<String> removeList = new ArrayList<>();
        for (CartGoods cartGoods : checkCartRequest.getCartGoodsList()) {
            String cartGoodsUid = cartGoods.getCartGoodsUid();
            if (keySet == null
                    || (!keySet.contains(cartGoods.getSpuId())
                    && !cartGoodsUid.startsWith(CommonsConstant.COUPON_PREFIX))) {
                removeList.add(cartGoodsUid);
                cartGoods.setCartGoodsUid(null);
            }
        }
        this.removeCartGoodId(checkCartRequest.getPartnerId(), checkCartRequest.getStoreId(), checkCartRequest.getTableNumber(), removeList);
    }

    @Override
    public void removeInvalidGoods(String partnerId, String storeId, String tableNumber, List<CartGoods> cartGoodsList, List<String> invalidGoodsIdList) {
        this.removeCartGoodId(partnerId, storeId, tableNumber, invalidGoodsIdList);
        cartGoodsList.removeIf(k -> invalidGoodsIdList.contains(k.getCartGoodsUid()) || StringUtils.isEmpty(k.getCartGoodsUid()));
    }
}
