#include <stdio.h>
#include <criterion/criterion.h>
#include "../src/cotp.h"

Test(b32_decode_test, b32_all_chars) {
    cotp_error_t err;
    const char *k = "IFCEMRZUGEZSDQVDEQSSMJRIFAXT6XWDU7B2SKS3LURSSLJOFR6DYPRL";
    const char *k_dec = "ADFG413!£$%&&((/?^çé*[]#)-.,|<>+";

    char *dk = base32_decode (k, strlen(k)+1, &err);

    cr_expect(strcmp(dk, k_dec) == 0, "Expected %s to be equal to %s", dk, k_dec);

    free(dk);
}


Test(b32_decode_test, b32_all_chars_noplusone) {
    cotp_error_t err;
    const char *k = "IFCEMRZUGEZSDQVDEQSSMJRIFAXT6XWDU7B2SKS3LURSSLJOFR6DYPRL";
    const char *k_dec = "ADFG413!£$%&&((/?^çé*[]#)-.,|<>+";

    char *dk = base32_decode (k, strlen(k), &err);

    cr_expect(strcmp(dk, k_dec) == 0, "Expected %s to be equal to %s", dk, k_dec);

    free(dk);
}


Test(b32_decode_test, b32_rfc4648) {
    cotp_error_t err;
    const char *k[] = {"", "MY======", "MZXQ====", "MZXW6===", "MZXW6YQ=", "MZXW6YTB", "MZXW6YTBOI======"};
    const char *k_dec[] = {"", "f", "fo", "foo", "foob", "fooba", "foobar"};

    for (int i = 0; i < 7; i++) {
        char *dk = base32_decode (k[i], strlen(k[i])+1, &err);
        cr_expect(strcmp(dk, k_dec[i]) == 0, "Expected %s to be equal to %s", dk, k_dec[i]);
        free(dk);
    }
}


Test(b32_decode_test, b32_rfc4648_noplusone) {
    cotp_error_t err;
    const char *k[] = {"", "MY======", "MZXQ====", "MZXW6===", "MZXW6YQ=", "MZXW6YTB", "MZXW6YTBOI======"};
    const char *k_dec[] = {"", "f", "fo", "foo", "foob", "fooba", "foobar"};

    for (int i = 0; i < 7; i++) {
        char *dk = base32_decode (k[i], strlen(k[i]), &err);
        cr_expect(strcmp(dk, k_dec[i]) == 0, "Expected %s to be equal to %s", dk, k_dec[i]);
        free(dk);
    }
}


Test(b32_decode_test, b32_invalid_input) {
    cotp_error_t err;
    const char *k = "£&/(&/";
    size_t len = strlen(k);

    uint8_t *dk = base32_decode (k, len, &err);

    cr_expect_null (dk, "%s");
    cr_expect_eq (err, INVALID_B32_INPUT);
}


Test(b32_decode_test, b32_decode_input_exceeded) {
    cotp_error_t err;
    const char *k = "ASDF";
    size_t len = 128*1024*1024;

    uint8_t *dk = base32_decode (k, len, &err);

    cr_expect_null (dk, "%s");
    cr_expect_eq (err, INVALID_USER_INPUT);
}


Test(b32_decode_test, b32_decode_input_whitespaces) {
    cotp_error_t err;
    const char *k = "MZ XW 6Y TB";
    const char *expected = "fooba";

    uint8_t *dk = base32_decode (k, strlen(k), &err);

    cr_expect_str_eq (dk, expected, "%s");
}


Test(b32_decode_test, b32_decode_encode_null) {
    const char* token = "LLFTSZYMUGKHEDQBAAACAZAMUFKKVFLS";
    cotp_error_t err;

    uint8_t* binary = base32_decode (token, strlen(token)+1, &err);
    cr_expect_eq (err, NO_ERROR);

    char* result = base32_encode (binary, 20, &err);
    cr_expect_eq (err, NO_ERROR);

    cr_expect_str_eq (result, token, "%s");
}


Test(b32_decode_test, b32_decode_empty_string) {
    cotp_error_t err;

    uint8_t* binary = base32_decode ("", 0, &err);
    cr_expect_eq (err, EMPTY_STRING);
    cr_expect_str_eq ((char *)binary, "", "%s");

    free (binary);
}


Test(b32_decode_test, byte_array_all_zeroes) {
    cotp_error_t err;
    const char *token = "AAAAAAA=";

    uint8_t* binary = base32_decode (token, strlen (token) + 1, &err);
    cr_expect_eq (err, NO_ERROR);
    for (int i=0;i<4;i++) {
        cr_expect_eq (binary[i], 0);
    }

    free (binary);
}


Test(b32_decode_test, b32_padding_in_middle) {
    cotp_error_t err;
    const char *k = "MY=W======";

    uint8_t *dk = base32_decode (k, strlen(k), &err);

    cr_expect_null (dk, "Expected NULL for padding in middle of string");
    cr_expect_eq (err, INVALID_B32_INPUT);
}


Test(b32_decode_test, b32_invalid_padding_count) {
    cotp_error_t err;
    // 2 padding chars is never valid in base32
    const char *k = "MZXW6Y==";

    uint8_t *dk = base32_decode (k, strlen(k), &err);

    cr_expect_null (dk, "Expected NULL for invalid padding count");
    cr_expect_eq (err, INVALID_B32_INPUT);
}


Test(b32_decode_test, b32_no_padding_valid) {
    cotp_error_t err;
    // No padding at all is accepted (common for OTP secrets)
    const char *k = "MZXW6YTBOI";
    const char *expected = "foobar";

    uint8_t *dk = base32_decode (k, strlen(k), &err);

    cr_expect_eq (err, NO_ERROR);
    cr_expect_str_eq ((char *)dk, expected, "Expected %s to be equal to %s", dk, expected);

    free (dk);
}


// Regression: caller-supplied data_len longer than the actual NUL-terminated
// content (e.g., embedded NUL inside the buffer) must not over-read.
// Discovered by libFuzzer.
Test(b32_decode_test, b32_decode_embedded_nul_no_overread) {
    cotp_error_t err = NO_ERROR;
    char input[40];
    memset (input, 0, sizeof (input));
    input[0] = 'C';   // valid base32 char
    input[31] = '?';  // garbage past the embedded NUL — must NOT be read

    // Tell the function the buffer is 32 bytes even though strlen is 1.
    uint8_t *out = base32_decode (input, sizeof (input), &err);
    // We don't care which error code (or success) — only that it doesn't crash
    // and doesn't read past byte 1 of the input.
    free (out);
}