/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 "benchmark/cli_option_parsing.h"

#include <cstdint>
#include <string>
#include <utility>
#include <vector>

#include "gtest/gtest.h"
#include "paimon/testing/utils/testharness.h"

namespace paimon::testing {
namespace {

struct ArgvHolder {
    std::vector<std::string> args;
    std::vector<char*> argv;

    explicit ArgvHolder(std::vector<std::string> in_args) : args(std::move(in_args)) {
        argv.reserve(args.size());
        for (auto& arg : args) {
            argv.push_back(arg.data());
        }
    }

    int32_t argc() const {
        return static_cast<int32_t>(argv.size());
    }
};

TEST(CliOptionParsingTest, ConsumeCliOptionWorks) {
    std::string value;
    ASSERT_TRUE(paimon::benchmark::ConsumeCliOption("--foo=bar", "--foo", &value));
    ASSERT_EQ(value, "bar");

    value.clear();
    ASSERT_FALSE(paimon::benchmark::ConsumeCliOption("--foo", "--foo", &value));
}

TEST(CliOptionParsingTest, ParseCommaSeparatedColumnsWorks) {
    ASSERT_OK_AND_ASSIGN(auto parsed,
                         paimon::benchmark::ParseCommaSeparatedColumns("id, name,age", "--cols"));
    ASSERT_EQ(parsed.size(), 3U);
    ASSERT_EQ(parsed[0], "id");
    ASSERT_EQ(parsed[1], "name");
    ASSERT_EQ(parsed[2], "age");
}

TEST(CliOptionParsingTest, ParseCommaSeparatedColumnsRejectsInvalidInput) {
    ASSERT_NOK(paimon::benchmark::ParseCommaSeparatedColumns("", "--cols"));
    ASSERT_NOK(paimon::benchmark::ParseCommaSeparatedColumns("id,", "--cols"));
    ASSERT_NOK(paimon::benchmark::ParseCommaSeparatedColumns("id,,name", "--cols"));
}

TEST(CliOptionParsingTest, ParseDelimitedOptionsWorks) {
    ASSERT_OK_AND_ASSIGN(
        auto parsed, paimon::benchmark::ParseDelimitedOptions("k1:v1;k2:v2", "--paimon_option"));
    ASSERT_EQ(parsed.size(), 2U);
    ASSERT_EQ(parsed[0], std::make_pair(std::string("k1"), std::string("v1")));
    ASSERT_EQ(parsed[1], std::make_pair(std::string("k2"), std::string("v2")));
}

TEST(CliOptionParsingTest, ParseDelimitedOptionsTrimsKeyAndValue) {
    ASSERT_OK_AND_ASSIGN(auto parsed, paimon::benchmark::ParseDelimitedOptions(" k1 : v1 ; k2: v2 ",
                                                                               "--paimon_option"));
    ASSERT_EQ(parsed.size(), 2U);
    ASSERT_EQ(parsed[0], std::make_pair(std::string("k1"), std::string("v1")));
    ASSERT_EQ(parsed[1], std::make_pair(std::string("k2"), std::string("v2")));
}

TEST(CliOptionParsingTest, ParseDelimitedOptionsRejectsInvalidInput) {
    ASSERT_NOK(paimon::benchmark::ParseDelimitedOptions("", "--paimon_option"));
    ASSERT_NOK(paimon::benchmark::ParseDelimitedOptions("k1:v1;", "--paimon_option"));
    ASSERT_NOK(paimon::benchmark::ParseDelimitedOptions("k1:", "--paimon_option"));
    ASSERT_NOK(paimon::benchmark::ParseDelimitedOptions(":v1", "--paimon_option"));
    ASSERT_NOK(paimon::benchmark::ParseDelimitedOptions("k1:   ", "--paimon_option"));
}

TEST(CliOptionParsingTest, ParseStringOptionArgWorksForEqualsAndSeparatedForms) {
    {
        ArgvHolder argv_holder({"prog", "--foo=bar"});
        int32_t arg_index = 1;
        std::string value;
        ASSERT_OK_AND_ASSIGN(bool is_parsed,
                             paimon::benchmark::ParseStringOptionArg(
                                 argv_holder.argc(), argv_holder.argv.data(),
                                 argv_holder.args[arg_index], "--foo", &arg_index, &value));
        ASSERT_TRUE(is_parsed);
        ASSERT_EQ(arg_index, 1);
        ASSERT_EQ(value, "bar");
    }

    {
        ArgvHolder argv_holder({"prog", "--foo", "bar"});
        int32_t arg_index = 1;
        std::string value;
        ASSERT_OK_AND_ASSIGN(bool is_parsed,
                             paimon::benchmark::ParseStringOptionArg(
                                 argv_holder.argc(), argv_holder.argv.data(),
                                 argv_holder.args[arg_index], "--foo", &arg_index, &value));
        ASSERT_TRUE(is_parsed);
        ASSERT_EQ(arg_index, 2);
        ASSERT_EQ(value, "bar");
    }
}

TEST(CliOptionParsingTest, ParseStringOptionArgRejectsMissingValue) {
    ArgvHolder argv_holder({"prog", "--foo"});
    int32_t arg_index = 1;
    std::string value;
    ASSERT_NOK(paimon::benchmark::ParseStringOptionArg(argv_holder.argc(), argv_holder.argv.data(),
                                                       argv_holder.args[arg_index], "--foo",
                                                       &arg_index, &value));
}

TEST(CliOptionParsingTest, ParseStringOptionArgIgnoresOtherOptions) {
    ArgvHolder argv_holder({"prog", "--bar=baz"});
    int32_t arg_index = 1;
    std::string value;
    ASSERT_OK_AND_ASSIGN(bool is_parsed,
                         paimon::benchmark::ParseStringOptionArg(
                             argv_holder.argc(), argv_holder.argv.data(),
                             argv_holder.args[arg_index], "--foo", &arg_index, &value));
    ASSERT_FALSE(is_parsed);
    ASSERT_EQ(arg_index, 1);
    ASSERT_TRUE(value.empty());
}

TEST(CliOptionParsingTest, ParseCommaSeparatedOptionArgAndDelimitedRepeatableOptionArgWorks) {
    {
        ArgvHolder argv_holder({"prog", "--cols", "id,name"});
        int32_t arg_index = 1;
        std::vector<std::string> columns;
        ASSERT_OK_AND_ASSIGN(bool is_parsed,
                             paimon::benchmark::ParseCommaSeparatedOptionArg(
                                 argv_holder.argc(), argv_holder.argv.data(),
                                 argv_holder.args[arg_index], "--cols", &arg_index, &columns));
        ASSERT_TRUE(is_parsed);
        ASSERT_EQ(arg_index, 2);
        ASSERT_EQ(columns.size(), 2U);
        ASSERT_EQ(columns[0], "id");
        ASSERT_EQ(columns[1], "name");
    }

    {
        ArgvHolder argv_holder({"prog", "--paimon_option", "k1:v1;k2:v2"});
        int32_t arg_index = 1;
        std::vector<std::pair<std::string, std::string>> options;
        ASSERT_OK_AND_ASSIGN(bool is_parsed, paimon::benchmark::ParseDelimitedRepeatableOptionArg(
                                                 argv_holder.argc(), argv_holder.argv.data(),
                                                 argv_holder.args[arg_index], "--paimon_option",
                                                 &arg_index, &options));
        ASSERT_TRUE(is_parsed);
        ASSERT_EQ(arg_index, 2);
        ASSERT_EQ(options.size(), 2U);
        ASSERT_EQ(options[0], std::make_pair(std::string("k1"), std::string("v1")));
        ASSERT_EQ(options[1], std::make_pair(std::string("k2"), std::string("v2")));
    }
}

}  // namespace
}  // namespace paimon::testing
