import {
  generateTotp, verifyTotp, base32Encode, base32Decode, generateSecret, buildOtpauthUri,
} from '../../services/auth/totp';

// RFC 6238 Appendix B — ASCII seed "12345678901234567890" (20 bytes), 8 digits.
const seed = base32Encode(Buffer.from('12345678901234567890', 'ascii'));
const vectors: Array<[number, string]> = [
  [59, '94287082'],
  [1111111109, '07081804'],
  [1111111111, '14050471'],
  [1234567890, '89005924'],
  [2000000000, '69279037'],
  [20000000000, '65353130'],
];

let fail = 0;
const check = (ok: boolean, label: string) => {
  if (!ok) fail++;
  console.log(`${ok ? 'PASS' : 'FAIL'}  ${label}`);
};

for (const [t, expected] of vectors) {
  const got = generateTotp(seed, { algorithm: 'SHA1', digits: 8, period: 30 }, t * 1000);
  check(got === expected, `RFC6238 t=${t} expected=${expected} got=${got}`);
}

const rt = base32Decode(base32Encode(Buffer.from('hello world!', 'utf8'))).toString('utf8');
check(rt === 'hello world!', `base32 roundtrip -> "${rt}"`);

const s = generateSecret();
check(/^[A-Z2-7]{32}$/.test(s), `generated secret shape -> ${s}`);

const opts = { algorithm: 'SHA1' as const, digits: 6, period: 30 };
const now = 1700000000000;
const codeNow = generateTotp(s, opts, now);

check(verifyTotp(s, codeNow, opts, 1, now).valid, 'current step accepted');
check(verifyTotp(s, codeNow, opts, 1, now + 30000).valid, 'previous step accepted (window 1)');
check(verifyTotp(s, codeNow, opts, 1, now - 30000).valid, 'next step accepted (clock ahead)');
check(!verifyTotp(s, codeNow, opts, 1, now + 120000).valid, 'outside window rejected');
check(!verifyTotp(s, codeNow, opts, 0, now + 30000).valid, 'window 0 rejects neighbouring step');
check(!verifyTotp(s, '12345', opts, 1, now).valid, 'wrong length rejected');
check(!verifyTotp(s, 'abcdef', opts, 1, now).valid, 'non-numeric rejected');
check(!verifyTotp(s, '', opts, 1, now).valid, 'empty rejected');
check(verifyTotp(s, ` ${codeNow} `, opts, 1, now).valid, 'surrounding whitespace tolerated');

const r = verifyTotp(s, codeNow, opts, 1, now);
check(typeof r.timeStep === 'number' && r.delta === 0, `timeStep=${r.timeStep} delta=${r.delta}`);

const lower = base32Decode(s.toLowerCase()).toString('hex');
check(lower === base32Decode(s).toString('hex'), 'lowercase manual entry decodes identically');
const spaced = base32Decode(s.replace(/(.{4})/g, '$1 ')).toString('hex');
check(spaced === base32Decode(s).toString('hex'), 'spaced manual entry decodes identically');

const uri = buildOtpauthUri({ secret: s, accountName: 'user@example.com', issuer: 'MengoEngine' });
check(uri.startsWith('otpauth://totp/MengoEngine%3Auser%40example.com?'), `otpauth uri -> ${uri}`);
check(uri.includes('algorithm=SHA1') && uri.includes('digits=6') && uri.includes('period=30'), 'uri carries algorithm/digits/period');

console.log(fail === 0 ? '\nAll checks passed.' : `\n${fail} check(s) FAILED.`);
process.exit(fail ? 1 : 0);
