package cn.freemud.manager;

import cn.freemud.constant.RedisKeyConstant;
import cn.freemud.enums.OrderType;
import cn.freemud.enums.OrderTackCodeRuleEnum;
import com.freemud.api.assortment.datamanager.entity.db.AssortmentTackCodeRule;
import com.freemud.api.assortment.datamanager.manager.AssortmentTackCodeRuleManager;
import com.freemud.sdk.api.assortment.order.request.order.ConfirmOrderRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * @author freemud
 * @title: OrderTackCodeManager
 * @projectName order-group
 * @description: TODO
 * @date 2021/5/31上午9:36
 */
@Slf4j
@Component
public class OrderTackCodeManager {
    @Autowired
    private AssortmentTackCodeRuleManager assortmentTackCodeRuleManager;
    @Autowired
    protected RedisTemplate redisTemplate;

    public final static String numberChar = "0123456789";

    public final static String numberCharWithoutZero = "12345";



    /**
     * 生成取餐码
     *
     * @param partnerId 商户ID
     * @param storeId   门店ID
     * @return
     */
    public String generateTackCode(Integer orderType, String partnerId, String storeId, ConfirmOrderRequest var1, Function<Integer, String> fun) {
        String key = this.getOrderTackCodeKey(partnerId);
        String randomKey = getRandomOrderTackCodeKey(partnerId, storeId);
        String rule = (String) redisTemplate.opsForHash().entries(key).get("rule");
        AssortmentTackCodeRule assortmentTackCodeRule = null;
        if (Objects.isNull(rule) || rule.isEmpty()) {
            assortmentTackCodeRule = assortmentTackCodeRuleManager.queryTackCodeRule(partnerId);
            if (Objects.isNull(assortmentTackCodeRule)) {
                rule = String.valueOf(OrderTackCodeRuleEnum.RANDOM_NUMBER_ONE.getCode());
            } else {
                rule = assortmentTackCodeRule.getRule().toString();
            }
            redisTemplate.opsForHash().put(key, "rule", rule);
            redisTemplate.expire(key, 24, TimeUnit.HOURS);
        }
        if(!Objects.isNull(var1)){var1.setMealCodeRule(rule);}
        if (StringUtils.isNotEmpty(rule) && rule.equals(OrderTackCodeRuleEnum.RANDOM_NUMBER.getCode()+"")) {
            //6位码
            String number = generateNumber(6);
            if (!redisTemplate.opsForHash().hasKey(randomKey, storeId + number)) {
                redisTemplate.opsForHash().put(randomKey, storeId + number, number);
                redisTemplate.expire(randomKey, getSecondsNextEarlyMorning(), TimeUnit.SECONDS);
                return number;
            }
        } else if (StringUtils.isNotEmpty(rule) && rule.equals(OrderTackCodeRuleEnum.THE_CUSTOM.getCode()+"")) {
            //规则自定义
            Map<Object, Object> tackCodeSettingMap = redisTemplate.opsForHash().entries(randomKey);
            if (tackCodeSettingMap.isEmpty()) {
                if (Objects.isNull(assortmentTackCodeRule)) {
                    assortmentTackCodeRule = assortmentTackCodeRuleManager.queryTackCodeRule(partnerId);
                }
                initTackCodeSettingHash(assortmentTackCodeRule,randomKey,tackCodeSettingMap);
            }
            //判断订单类型
            String head = "",code = "";
            OrderType orderTypeEnum = OrderType.getByCode(orderType);
            if(!Objects.isNull(orderTypeEnum)){
                String tackDesc = orderTypeEnum.getTackDesc();
                head = tackCodeSettingMap.get(tackDesc+"Head").toString();
                Integer length = (Integer) tackCodeSettingMap.get(tackDesc+"Length");
                code = tackCodeSettingMap.get(tackDesc+"Code").toString();
                String incrementCode = redisTemplate.opsForHash().increment(randomKey, tackDesc+"Code", 1).toString();
                if (incrementCode.length() > length) {
                    //处理并发情况下，同时含有超过上限的数字。在第一次已经变更为1之后，再次塞1进缓存导致取餐吗重复。暂时无效，后续调整
                    if(incrementCode.length() < length){
                        code = redisTemplate.opsForHash().increment(randomKey, tackDesc+"Code", 1).toString();
                    }else{
                        redisTemplate.opsForHash().put(randomKey, tackDesc + "Code", 1);
                    }
                }
                code = String.format("%0" + length + "d", Integer.parseInt(code));
            }
            return head + code;
        } else {
            if (!redisTemplate.opsForHash().hasKey(key, storeId)) {
                redisTemplate.opsForHash().put(key, storeId, Integer.parseInt(generateNumber(4)));
                redisTemplate.expire(key, 24, TimeUnit.HOURS);
            }
        }
        return fun.apply(redisTemplate.opsForHash().increment(key, storeId, 1).intValue());
    }

    public String getOrderTackCodeKey(String partnerId) {
        Date currentDay = new Date();
        String todayStr = formatDate(currentDay);
        StringBuilder key = new StringBuilder(RedisKeyConstant.PAY_SUCCESS_TACK_CODE_KEY)
                .append(partnerId)
                .append(":")
                .append(todayStr);
        return key.toString();
    }

    /**
     * 获取取餐码key
     *
     * @param partnerId
     * @param storeId
     * @return
     */
    public String getRandomOrderTackCodeKey(String partnerId, String storeId) {
        Date currentDay = new Date();
        String todayStr = formatDate(currentDay);
        StringBuilder key = new StringBuilder(RedisKeyConstant.PAY_SUCCESS_TACK_CODE_KEY)
                .append(partnerId)
                .append(":")
                .append(storeId)
                .append(":")
                .append(todayStr);
        return key.toString();
    }

    public static Long getSecondsNextEarlyMorning() {
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DAY_OF_YEAR, 1);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return (cal.getTimeInMillis() - System.currentTimeMillis()) / 1000;
    }


    public static SimpleDateFormat defaultDateFormater = new SimpleDateFormat("yyyy-MM-dd");

    private String formatDate(Date date) {
        return defaultDateFormater.format(date);
    }


    public String generateNumber(int length) {
        StringBuffer sb = new StringBuffer();
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            if (i == 0) {
                sb.append(numberCharWithoutZero.charAt(random.nextInt(numberCharWithoutZero.length())));
            } else {
                sb.append(numberChar.charAt(random.nextInt(numberChar.length())));
            }
        }
        return sb.toString();
    }

    private void initTackCodeSettingHash(AssortmentTackCodeRule assortmentTackCodeRule,String randomKey, Map<Object, Object> tackCodeSettingMap){
        tackCodeSettingMap.put("eatInHead", assortmentTackCodeRule.getEatInHead());
        tackCodeSettingMap.put("eatInCode", Integer.valueOf(assortmentTackCodeRule.getEatInCode()));
        tackCodeSettingMap.put("eatInLength", assortmentTackCodeRule.getEatInCode().length());
        tackCodeSettingMap.put("collectGoodsHead", assortmentTackCodeRule.getCollectGoodsHead());
        tackCodeSettingMap.put("collectGoodsCode", Integer.valueOf(assortmentTackCodeRule.getCollectGoodsCode()));
        tackCodeSettingMap.put("collectGoodsLength", assortmentTackCodeRule.getCollectGoodsCode().length());
        tackCodeSettingMap.put("takeOutHead", assortmentTackCodeRule.getTakeOutHead());
        tackCodeSettingMap.put("takeOutCode", Integer.valueOf(assortmentTackCodeRule.getTakeOutCode()));
        tackCodeSettingMap.put("takeOutLength", assortmentTackCodeRule.getTakeOutCode().length());
        redisTemplate.opsForHash().putAll(randomKey, tackCodeSettingMap);
        redisTemplate.expire(randomKey, 24, TimeUnit.HOURS);
    }

}
