package org.springblade.common.utils;

import cn.hutool.core.lang.Assert;
import org.springblade.common.enums.ResCode;
import org.springblade.common.exception.BizException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.lang.reflect.UndeclaredThrowableException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.util.Date;

/**
 * @author: lianghanqiang
 * @description:	TOP 一次性加密口令
 * @since: 8/2/21 -- 10:48 AM
 */
public class OtpUtils {
//	649957
	public static void main(String[] args) {
		System.out.println(generateMyTOTP("1423159010830884865"));
	}

	/**
	 * 	id变形常量
	 * */
	private static final Long TRANSFORM_PARAMS = 95963L;

	/**
	 * 共享密钥
	 */
	private static final String SECRET_KEY = "";

	/**
	 * 时间步长 单位:毫秒 作为口令变化的时间周期
	 */
	private static final long STEP = 60000;

	/**
	 * 转码位数 [1-8]
	 */
	private static final int CODE_DIGITS = 6;

	/**
	 * 初始化时间
	 */
	private static final long INITIAL_TIME = 0;


	/**
	 * 数子量级
	 */
	private static final int[] DIGITS_POWER = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};

	private OtpUtils() {

	}

	/**
	 * 生成一次性密码
	 *
	 * @param id
	 * @return String
	 */
	public static String generateMyTOTP(String id) {
		Assert.notBlank(id,()-> new BizException(ResCode.ID_NOT_NULL));
		long now = new Date().getTime();
		String time = Long.toHexString(timeFactor(now)).toUpperCase();
		return generateTOTP(id + SECRET_KEY, time);
	}



	/**
	 * 获取动态因子
	 *
	 * @param targetTime 指定时间
	 * @return long
	 */
	private static long timeFactor(long targetTime) {
		return (targetTime - INITIAL_TIME) / STEP;
	}

	/**
	 * 哈希加密
	 *
	 * @param crypto   加密算法
	 * @param keyBytes 密钥数组
	 * @param text     加密内容
	 * @return byte[]
	 */
	private static byte[] hmac_sha(String crypto, byte[] keyBytes, byte[] text) {
		try {
			Mac hmac;
			hmac = Mac.getInstance(crypto);
			SecretKeySpec macKey = new SecretKeySpec(keyBytes, "AES");
			hmac.init(macKey);
			return hmac.doFinal(text);
		} catch (GeneralSecurityException gse) {
			throw new UndeclaredThrowableException(gse);
		}
	}

	private static byte[] hexStr2Bytes(String hex) {
		byte[] bArray = new BigInteger("10" + hex, 16).toByteArray();
		byte[] ret = new byte[bArray.length - 1];
		System.arraycopy(bArray, 1, ret, 0, ret.length);
		return ret;
	}

	private static String generateTOTP(String key, String time) {
		return generateTOTP(key, time, "HmacSHA1");
	}


	private static String generateTOTP(String key, String time, String crypto) {
		StringBuilder timeBuilder = new StringBuilder(time);
		while (timeBuilder.length() < 16)
			timeBuilder.insert(0, "0");
		time = timeBuilder.toString();

		byte[] msg = hexStr2Bytes(time);
		byte[] k = key.getBytes();
//		byte[] k = Base32.decode(key);
		byte[] hash = hmac_sha(crypto, k, msg);
		return truncate(hash);
	}

	/**
	 * 截断函数
	 *
	 * @param target 20字节的字符串
	 * @return String
	 */
	private static String truncate(byte[] target) {
		StringBuilder result;
		int offset = target[target.length - 1] & 0xf;
		int binary = ((target[offset] & 0x7f) << 24)
			| ((target[offset + 1] & 0xff) << 16)
			| ((target[offset + 2] & 0xff) << 8) | (target[offset + 3] & 0xff);

		int otp = binary % DIGITS_POWER[CODE_DIGITS];
		result = new StringBuilder(Integer.toString(otp));
		while (result.length() < CODE_DIGITS) {
			result.insert(0, "0");
		}
		return result.toString();
	}


	/**
	 * 	校验口令
	 * */
	public static boolean validate(String id,String code) {
		String s = generateMyTOTP(id);
		return code.equals((Long.valueOf(id))+s);
	}


	/**
	 * 	根据code获取用户Id
	 * */
	public static String getIdFormCode(String code) {
		return Long.valueOf(code.substring(0,19))+"";
	}

}
