package com.ruoyi.common.utils;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.atomic.AtomicInteger;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Component;

import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
/**
 * <pre>
 * 公鸡母鸡身份编号生成和校验工具
 *  chickenUtil.generate(公鸡数量,母鸡数量),返回批次号,公鸡身份编号数组,母鸡身份编号数组
 *  chickenUtil.validate(身份编号),验证失败抛出异常
 * 配置chicken.key可以修改验证加密的秘钥
 * Author : J.L.Zhou
 * E-Mail : 2233875735@qq.com
 * Tel : 151 1104 7708
 * Date : 2023年9月1日 下午4:47:21
 * Version : 1.0
 * Copyright 2023 jlzhou.top Inc. All rights reserved.
 * Warning: this content is only for internal circulation of the company.
 *          It is forbidden to divulge it or use it for other commercial purposes.
 * </pre>
 */
@Component
public class ChickenUtil {

	/**
	 * 分布式顺序ID生成器需要依赖redis
	 */
	private final StringRedisTemplate redisTemplate;

	/**
	 * 自增ID
	 */
	private RedisAtomicLong counter;

	private static final String PREFIX_ID = "ChickenUtil:id";
	private static final String PREFIX_DAY = "ChickenUtil:day";

	private LocalDate day;

	private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");

	@Value("${chicken.key:82317d9812f24b4d908e60182c70f823}")
	private String key;
	private HMac hmac;

	public ChickenUtil(StringRedisTemplate redisTemplate) {
		this.redisTemplate = redisTemplate;
	}

	@PostConstruct
	public void init() {
		hmac = new HMac(HmacAlgorithm.HmacSHA256, key.getBytes());
	}

	/**
	 * 生成批次的鸡身份编号
	 * 
	 * @param cockNum - 公鸡数量
	 * @param henNum  - 母鸡数量
	 * @return
	 */
	public Result generate(int cockNum, int henNum) {
		if (cockNum + henNum > 99999) {
			throw new RuntimeException("同一批次的鸡的数量不能大于99999");
		}

		Result r = new Result();
		// 获取日期内的分布式id顺序生成器
		if (counter == null) {
			counter = new RedisAtomicLong(PREFIX_ID, redisTemplate.getConnectionFactory());
		}
		if (day == null) {
			String dayStr = redisTemplate.opsForValue().get(PREFIX_DAY);
			if (dayStr == null) {
				day = LocalDate.now();
				redisTemplate.opsForValue().set(PREFIX_DAY, day.format(formatter));
			} else {
				day = LocalDate.parse(dayStr, formatter);
			}
		}
		if (!LocalDate.now().isEqual(day)) {
			day = LocalDate.now();
			redisTemplate.opsForValue().set(PREFIX_DAY, day.format(formatter));
			counter.set(0L);
		}

		long id = counter.incrementAndGet();
		if (id > 99) {
			throw new RuntimeException("每天只能产生99个批次");
		}

		StringBuffer sb = new StringBuffer();
		sb.append(day.format(formatter));
		if (id < 10) {
			sb.append(0);
		}
		sb.append(id);
		r.bid = Long.valueOf(sb.toString());

		AtomicInteger count = new AtomicInteger(0);

		r.cocks = new Long[cockNum];
		for (int i = 0; i < cockNum; i++) {
			String num = sb.toString()+String.format("%05d1", count.incrementAndGet());
			num += getValidate(num);
			r.cocks[i]=Long.valueOf(num);
		}

		r.hens = new Long[henNum];
		for (int i = 0; i < henNum; i++) {
			String num = sb.toString()+String.format("%05d0", count.incrementAndGet());
			num += getValidate(num);
			r.hens[i]=Long.valueOf(num);
		}
		return r;
	}
	
	/**
	 * 验证鸡身份编号是否合法
	 * @param id
	 * @exception RuntimeException 验证失败原因
	 */
	public void validate(Long id)throws RuntimeException {
		String num = Long.toString(id);
		if(num.length()!=18) {
			throw new RuntimeException("身份编号必须由18位的数字组成");
		}
		String ret = getValidate(num.substring(0,16));
		if(!ret.equals(num.substring(16))) {
			 new RuntimeException("身份编号校验失败");
		}
	}

	/**
	 * 获取两位的验证字符
	 * 
	 * @param num - 需要验证的数字字符串
	 * @return
	 */
	private String getValidate(String num) {
		byte[] bs = hmac.digest(num);
		int ret = 0;
		for (int i = 0; i < 5 && i < bs.length; i++) {
			ret += bs[i];
		}
		ret %= 100;
		ret = Math.abs(ret);
		return ret < 10 ? ("0" + ret) : Integer.toString(ret);
	}

	public static class Result {
		Long bid;
		Long[] cocks;
		Long[] hens;

		public Long getBid() {
			return bid;
		}

		public Long[] getCocks() {
			return cocks;
		}

		public Long[] getHens() {
			return hens;
		}
	}
	
	public static void main(String[] args) {
		System.out.println(Long.toString(202309010200008159L, 36).length());
	}
}
