/*
 * Copyright 2020-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mongocrypt-kek-private.h"
#include "test-mongocrypt-assert-match-bson.h"
#include "test-mongocrypt.h"
#include <bson/bson.h>

static void _run_one_test(_mongocrypt_tester_t *tester, bson_t *test) {
    bson_iter_t iter;
    bson_t input;
    char *input_str;
    mongocrypt_status_t *status;
    _mongocrypt_kek_t kek;
    const char *expect;
    bool ret;
    bson_t out;
    const char *expect_append;

    status = mongocrypt_status_new();
    memset(&kek, 0, sizeof(_mongocrypt_kek_t));
    BSON_ASSERT(bson_iter_init_find(&iter, test, "input"));
    bson_iter_bson(&iter, &input);
    BSON_ASSERT(bson_iter_init_find(&iter, test, "expect"));
    expect = bson_iter_utf8(&iter, NULL);

    /* Tests may include an optional different expectation for calling
     * _mongocrypt_kek_append. */
    if (bson_iter_init_find(&iter, test, "expect_append")) {
        expect_append = bson_iter_utf8(&iter, NULL);
    } else {
        expect_append = expect;
    }

    input_str = bson_as_relaxed_extended_json(&input, NULL);
    TEST_PRINTF("- testcase: %s\n", input_str);
    bson_free(input_str);

    ret = _mongocrypt_kek_parse_owned(&input, &kek, status);
    if (0 == strcmp(expect, "ok")) {
        ASSERT_OK_STATUS(ret, status);
        bson_init(&out);
        ret = _mongocrypt_kek_append(&kek, &out, status);
        if (0 == strcmp(expect_append, "ok")) {
            _mongocrypt_kek_t kek_copy;

            ASSERT_OK_STATUS(ret, status);
            /* This should round trip. */
            _assert_match_bson(&out, &input);

            /* Check that copy works as well. */
            bson_reinit(&out);
            _mongocrypt_kek_copy_to(&kek, &kek_copy);
            ret = _mongocrypt_kek_append(&kek_copy, &out, status);
            ASSERT_OK_STATUS(ret, status);
            _assert_match_bson(&out, &input);
            _mongocrypt_kek_cleanup(&kek_copy);
        } else {
            ASSERT_FAILS_STATUS(ret, status, expect_append);
        }

        bson_destroy(&out);
    } else {
        ASSERT_FAILS_STATUS(ret, status, expect);
    }

    _mongocrypt_kek_cleanup(&kek);
    mongocrypt_status_destroy(status);
}

static void test_mongocrypt_kek_parsing(_mongocrypt_tester_t *tester) {
    bson_t test_file;
    bson_iter_t iter;

    _load_json_as_bson("./test/data/kek-tests.json", &test_file);
    for (bson_iter_init(&iter, &test_file); bson_iter_next(&iter);) {
        bson_t test;

        bson_iter_bson(&iter, &test);
        _run_one_test(tester, &test);
        bson_destroy(&test);
    }
    bson_destroy(&test_file);
}

void _mongocrypt_tester_install_kek(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_mongocrypt_kek_parsing);
}
