import { decode } from "./base32decoder.js";
import jsSHA from "jssha";
"use bigint"
/**
 * get HOTP based on counter
 * @param {BigInt} counter BigInt counter of HOTP
 * @param {string} secret base32 encoded string 
 * @param {number} [digits=6] number of digits in OTP token
 * @param {string} [hashType='SHA-1'] type of hash (more in jsSHA documentation)
 * @returns HOTP string
 */
export function getHOTP(counter, secret, digits = 6, hashType = 'SHA-1'){

    //Stage 1: Prepare data
    const rawDataCounter = new DataView(new ArrayBuffer(8))
    rawDataCounter.setUint32(4, counter)
    
    const bCounter = new Uint8Array(rawDataCounter.buffer)
    const bSecret = new Uint8Array(decode(secret).match(/.{1,2}/g).map(chunk => parseInt(chunk, 16))); //confirmed
    
    //Stage 2: Hash data
    const jssha = new jsSHA(hashType, 'UINT8ARRAY')
    jssha.setHMACKey(bSecret, 'UINT8ARRAY')
    jssha.update(bCounter)
    const hmacResult = jssha.getHMAC('UINT8ARRAY') //confirmed
    
    //Stage 3: Dynamic truncate
    const offsetB = hmacResult[19] & 0xf;
    const P = hmacResult.slice(offsetB, offsetB + 4)
    P[0] = P[0] & 0x7f;
    
    //Stage 4: Format string
    let res = (new DataView(P.buffer).getInt32(0) % Math.pow(10, digits)).toString()
    while(res.length < digits)
        res = '0' + res;
    return res;
}

/**
 * get OTP based on current time
 * @param {string} secret base32 encoded string 
 * @param {number} [digits=6] digits in OTP
 * @param {number} [time=Date.now()] time for counter (default unix time epoch)
 * @param {number} [fetchTime=30] period of token in seconds
 * @param {number} [timeOffset=0] time offset for token in seconds 
 * @param {string} [hashType='SHA-1'] type of hash (more in jsSHA documentation)
 * @returns TOTP string
 */
export function getTOTP(secret, digits = 6, time = Date.now(), fetchTime = 30, timeOffset = 0, hashType = 'SHA-1')
{
    const unixTime = Math.round((time / 1000 + timeOffset) / fetchTime)
    return getHOTP(BigInt(unixTime), secret, digits)
}