import 'package:test/test.dart';
import 'package:pb_authenticator_totp/totp.dart';
import 'package:pb_authenticator_totp/totp_algorithm.dart';
import 'package:pb_authenticator_totp/otp_uri.dart';

void main() {
  // https://datatracker.ietf.org/doc/html/rfc4648#section-10
  // https://stackoverflow.com/a/54263961
  test('Base32', () {
    expect(Base32.decode("MY======"), "f".codeUnits);
    expect(Base32.decode("MZXQ===="), "fo".codeUnits);
    expect(Base32.decode("MZXW6==="), "foo".codeUnits);
    expect(Base32.decode("MZXW6YQ="), "foob".codeUnits);
    expect(Base32.decode("MZXW6YTB"), "fooba".codeUnits);
    expect(Base32.decode("MZXW6YTBOI======"), "foobar".codeUnits);
    expect(Base32.isBase32("MZXW6YTBOI======"), true);
    expect(Base32.decode("AE======"), "\x01".codeUnits);
    expect(Base32.decode("AA======"), "\x00".codeUnits);
  });

  test('TOTP - Key URI 1', () {
    var parsed = OtpUri.fromUri(
        "otpauth://totp/Example:alice@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Example");
    expect(parsed.accountName, "alice@example.com");
    expect(parsed.algorithm, OtpHashAlgorithm.sha1);
    expect(parsed.digits, 6);
    expect(parsed.issuer, "Example");
    expect(parsed.period, 30);
    expect(parsed.secret, "JBSWY3DPEHPK3PXP");
  });

  test('TOTP - Key URI Complete', () {
    var parsed = OtpUri.fromUri(
        "otpauth://totp/ACME%20Co:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30");
    expect(parsed.accountName, "john.doe@email.com");
    expect(parsed.algorithm, OtpHashAlgorithm.sha1);
    expect(parsed.digits, 6);
    expect(parsed.issuer, "ACME Co");
    expect(parsed.period, 30);
    expect(parsed.secret, "HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ");
  });

  test('TOTP - Bad Key URI', () {
    expect(
        () => OtpUri.fromUri(
            "otpauth://totp/Example:alice@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Example&digits=12"),
        throwsA(allOf(
            isFormatException,
            predicate((e) =>
                e is FormatException && e.message == "Incorrect parameters"))));
  });

  test('TOTP - Bad Algorithm', () {
    expect(
        () => OtpUri.fromUri(
            "otpauth://totp/Example:alice@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Example&digits=12&algorithm=sha??"),
        throwsA(allOf(
            isFormatException,
            predicate((e) =>
                e is FormatException &&
                e.message == "Unrecognised algorithm"))));
  });

  test('TOTP - 1542791843', () {
    expect(Totp.generateCode(1542791843, "JBSWY3DPEHPK3PXP"), "092264");
  });

  test('TOTP - 59', () {
    expect(Totp.generateCode(59, "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ", 8),
        "94287082");
  });

  test('TOTP - 20000000000', () {
    expect(
        Totp.generateCode(20000000000, "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ", 8),
        "65353130");
  });

  test('TOTP - Multiple', () {
    expect(
        Totp.generateCodes(
            [59, 20000000000], "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ", 8),
        ["94287082", "65353130"]);
  });

  test('TOTP Item attributes', () {
    var secret = "A";
    var digits = 8;
    var period = 60;
    var algorithm = OtpHashAlgorithm.sha256;
    var issuer = "bar";
    var accountName = "foo@bar";
    var item = TotpItem(secret, digits, period, algorithm, issuer, accountName);
    expect(item.secret, secret);
    expect(item.digits, digits);
    expect(item.period, period);
    expect(item.algorithm, algorithm);
    expect(item.issuer, issuer);
    expect(item.accountName, accountName);
  });

  test('TOTP Item equality', () {
    var item1 = TotpItem("A");
    var item2 = TotpItem("A");
    var item3 = TotpItem("B");
    // Same object
    expect(item1 == item1, true);
    // Same secret (and other fields apart from id), so still equal
    expect(item1 == item2, true);
    // Secret changed
    expect(item1 == item3, false);
  });
}
