
package com.tmerclub.cloud.common.leaf.manager;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.format.FastDateFormat;
import cn.hutool.core.util.StrUtil;
import com.tmerclub.cloud.common.leaf.common.Result;
import com.tmerclub.cloud.common.leaf.common.Status;
import com.tmerclub.cloud.common.leaf.constant.DistributedIdSysType;
import com.tmerclub.cloud.common.leaf.constant.GlobalDistributedIdKey;
import com.tmerclub.cloud.common.leaf.exception.LeafServerException;
import com.tmerclub.cloud.common.leaf.exception.NoKeyException;
import com.tmerclub.cloud.common.leaf.service.SegmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;


/**
 * @author tmerclub
 * @date 2020/7/15
 */
@Service
public class SegmentManager {

    public static final String DATETIME_PATTERN = "yyMMdd";

    public static final FastDateFormat DATETIME_FORMAT = FastDateFormat.getInstance(DATETIME_PATTERN);

    /**
     * 分片id长度
     */
    public static final int USER_SPLIT_LENGTH = 3;
    /**
     * 日期长度
     */
    public static final int DATE_LENGTH = 6;
    /**
     * id默认长度
     */
    public static final int ID_DEFAULT_LENGTH = 15;

    @Autowired
    private SegmentService segmentService;

    @Value("${application.workerId:}")
    private String workerId;

    @Value("${spring.application.name}")
    private String appname;

    /**
     * 获取id，从1开始递增
     * @param key
     * @return
     */
    public Long getSegmentId(String key) {
        // 默认我们给个15位
        return genNo(key, ID_DEFAULT_LENGTH);
    }
    public Long getSegmentId(String key, Integer length) {
        return genNo(key, length);
    }

    /**
     * 获取随机码，用于提货码、核销之类需要固定长度的场景
     * Long类型返回的id，前面无法填充0，导致长度没有固定
     * @param key
     * @return
     */
    public String getSegmentCode(String key) {
        return genNoStr(key, ID_DEFAULT_LENGTH, null);
    }

    /**
     * 获取随机码，用于提货码、核销之类需要固定长度的场景
     * Long类型返回的id，前面无法填充0，导致长度没有固定
     * @param key
     * @param length id的长度
     * @return
     */
    public String getSegmentCode(String key, Integer length) {
        return genNoStr(key, length, null);
    }

    /**
     * 批量获取-从1开始递增
     * @param key
     * @param size
     * @return
     */;
    public List<Long> listSegmentId(String key, Integer size) {
        List<Long> ids = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            ids.add(getSegmentId(key));
        }
        return ids;
    }

    /**
     * 生成携带日期格式的id（非分库分表类型id）
     * 默认长度为15位，也是从1开始递增，但前面拼接了日期，所以长度固定，中间使用0来填充，每日可生成的id数量为十亿
     * @param key
     * @return
     */
    public Long getDateFormatSegmentId(String key) {
        return getDateFormatSegmentId(key, ID_DEFAULT_LENGTH);
    }
    public Long getDateFormatSegmentId(String key, Integer length) {
        String id = getDateTime() + genNo(key, length - DATE_LENGTH);
        return Long.parseLong(id);
    }

    /**
     * 用于分库分表的分片字段id
     * 初始化到数据库中的key格式为： key名称 + id的后三位， 所以key总数量为1000
     * @param key
     * @param id
     * @return
     */
    public Long getSegmentIdWithDateTime(String key, Long id) {
        // 默认生成转换为16位长度的id，js long的精度是16位的，所以默认我们也给个16位 6 + 7 + 最后3位是userId这很关键
        // 这里的7位id其中有x位用来区分测试环境和生产环境，还有7-x位用来生成id，如果是全局id，7-（x+1）位
        // 所以workerId最好是一位数，或者没有
        String idSuf = getUserIdKey(String.valueOf(id));
        return Long.valueOf(getDateTime() + genNoStr(key, 7, idSuf) + idSuf);
    }

    private Long genNo(String key, int length) {
        // 全局的分布式id键
        if (GlobalDistributedIdKey.containKey(key)) {
            return Long.parseLong(getGlobalId(key, length));
        }
        Long nextId = get(key, length);
        if (StrUtil.isBlank(workerId)) {
            return nextId;
        }
        return Long.parseLong(workerId + nextId);
    }

    private String genNoStr(String name, int length, String idSuf){
        String key = name;
        if (Objects.nonNull(idSuf)) {
            // 拼接key - key名称后面拼接上id， 拼接符：-, 每个id都有对应的key
            key = name + StrUtil.DASHED + idSuf;
        }
        // 全局的分布式id键
        if (GlobalDistributedIdKey.containKey(name)) {
            return getGlobalId(key, length);
        }
        Long nextId = get(key, length);
        // 在id前面补零， length为补零后的长度， d:表示为正数类型
        length = length - workerId.length();
        String newId = String.format("%0" + length + "d", nextId);
        return workerId + newId;
    }

    private String getGlobalId(String key, int length) {
        // 获取当前服务id, id为空就加一个服务名称对应的枚举类型
        Integer systemId = DistributedIdSysType.getId(appname);
        // length - 1 是为了留一个位置出来补足 systemId
        length = length - 1;
        Long nextId = get(key, length);
        // 在id前面补零， length为补零后的长度， d:表示为正数类型
        length = length - workerId.length();
        // 在id前面补零， length为补零后的长度， d:表示为正数类型
        String newId = String.format("%0" + length + "d", nextId);
        return systemId + workerId + newId;
    }


    private Long get(String key, Integer length) {
        // length - x 是为了留位置出来补足workId
        length = length - workerId.length();
        Result result;
        if (key == null || key.isEmpty()) {
            throw new NoKeyException();
        }
        Result id = segmentService.getId(key, length);
        result = id;
        if (Objects.equals(result.getStatus(), Status.EXCEPTION)) {
            throw new LeafServerException(result.toString());
        }
        return result.getId();
    }


    private String getDateTime() {
        return DateUtil.format(new Date(), DATETIME_FORMAT);
    }


    /**
     * 截取用户ID的后三位
     */
    private String getUserIdKey(String userId) {
        // 如果userId的长度大于或等于3，则直接返回
        if (userId.length() >= USER_SPLIT_LENGTH) {
            return userId.substring(userId.length() - USER_SPLIT_LENGTH);
        }
        // 如果userId的长度大于或等于3，则直接前面补0
        StringBuilder userIdKey = new StringBuilder(userId);
        while (userIdKey.length() != USER_SPLIT_LENGTH) {
            userIdKey.insert(0, "0");
        }
        return userIdKey.toString();
    }

    /**
     * 生成key列表sql
     * @param args
     */
    public static void main(String[] args) {
        String begin = "INSERT INTO `leaf_alloc` (`biz_tag`, `max_id`, `step`, `description`) VALUES";
        System.out.println(begin);
        loadKeySql("tmerclub-group-team", "团购队伍id");
        loadKeySql("tmerclub-group-order", "团购订单号");
        loadKeySql("tmerclub-refund", "退款id");
        // 生成的插入sql会在最后多出一个逗号，自行去掉即可
    }

    private static void loadKeySql(String key, String value) {
        for (int i = 0; i < 1000; i++) {
            String id = String.valueOf(i);
            if (id.length() == 1) {
                id = "00" + id;
            } else if (id.length() == 2) {
                id = "0" + id;
            }
            String sql = "('" + key + "-" + id + "','1','1000','" + value + "'),";
            System.out.println(sql);
        }
    }
}
