use std::time::{SystemTime, UNIX_EPOCH};
use base32::Alphabet;
use byteorder::{BigEndian, ByteOrder};
use urlencoding::encode;
use hmac::{Hmac, Mac};
use rand::RngCore;
use sha1::Sha1;
type HmacSha1 = Hmac<Sha1>;
pub struct Totp {}
impl Totp {
pub fn create_bind_qrcode(issuer: &str, username: &str, secret: &str) -> String {
let issuer = encode(issuer).to_string();
format!("otpauth://totp/{issuer}:{username}?secret={secret}&issuer={issuer}")
}
pub fn generate_secret_base32(length: usize) -> String {
let mut bytes = vec![0u8; length];
rand::rng().fill_bytes(&mut bytes);
base32::encode(Alphabet::Rfc4648 { padding: false }, &bytes)
}
pub fn get_code(secret_base32: &str, step: i64) -> String {
let t = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64 % 30;
let time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64 + step - t;
let key = base32::decode(Alphabet::Rfc4648 { padding: false }, secret_base32).unwrap();
let counter = time / 30;
let mut counter_bytes = [0u8; 8];
BigEndian::write_u64(&mut counter_bytes, counter as u64);
let mut mac = HmacSha1::new_from_slice(&key).unwrap();
mac.update(&counter_bytes);
let hmac_result = mac.finalize().into_bytes();
let offset = (hmac_result[19] & 0x0f) as usize;
let bin_code = (((hmac_result[offset] & 0x7f) as u32) << 24) | ((hmac_result[offset + 1] as u32) << 16) | ((hmac_result[offset + 2] as u32) << 8) | (hmac_result[offset + 3] as u32);
let res = bin_code % 10u32.pow(6);
format!("{res:06}")
}
}