/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
 * ubs-virt-ovs is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include "test_config_manager.h"

#include "config_manager.h"

#include <filesystem>
#include <fstream>

using namespace virt::ovs::config;

namespace ovs::ut {

void TestConfigManager::SetUp() {}
void TestConfigManager::TearDown()
{
    GlobalMockObject::verify();
    GlobalMockObject::reset();
}

TEST_F(TestConfigManager, Trim_Basic)
{
    EXPECT_EQ(Trim("  hello  "), "hello");
    EXPECT_EQ(Trim(""), "");
    EXPECT_EQ(Trim(" \t\n "), "");
}

TEST_F(TestConfigManager, CatString_Basic)
{
    std::vector<std::string> vec = {"a", "b", "c"};
    EXPECT_EQ(CatString(vec, ","), "a,b,c");
    EXPECT_EQ(CatString({}, ","), "");
}

TEST_F(TestConfigManager, CheckNoIllegalChars_Basic)
{
    EXPECT_TRUE(CheckNoIllegalChars("valid_key-1"));
    EXPECT_FALSE(CheckNoIllegalChars("invalid key"));
    EXPECT_TRUE(CheckNoIllegalChars("192.168.1.1", true));
}

TEST_F(TestConfigManager, PathJoin_Basic)
{
    EXPECT_EQ(PathJoin("/tmp", "file.conf"), "/tmp/file.conf");
    EXPECT_EQ(PathJoin("/tmp/", "file.conf"), "/tmp/file.conf");
    EXPECT_EQ(PathJoin("", "file.conf"), "file.conf");
}

TEST_F(TestConfigManager, IsConfFile_Basic)
{
    EXPECT_TRUE(IsConfFile("test.conf"));
    EXPECT_FALSE(IsConfFile("test.txt"));
}

TEST_F(TestConfigManager, Format_Checks)
{
    Format fmt;
    EXPECT_TRUE(fmt.IsSectionStart("["));
    EXPECT_TRUE(fmt.IsSectionEnd("]"));
    EXPECT_TRUE(fmt.IsAssign("="));
    EXPECT_TRUE(fmt.IsComment(";"));
    EXPECT_TRUE(fmt.IsComment("#"));
}

TEST_F(TestConfigManager, ParseConf_Valid)
{
    ConfigManager mgr;
    std::string tempSection = "section1";
    mgr.ParseConf("file.conf", "key1=value1", 1, tempSection);

    std::string val;
    EXPECT_EQ(mgr.GetConf("section1", "key1", val), ConfigCode::OK);
    EXPECT_EQ(val, "value1");
}

TEST_F(TestConfigManager, ParseSection_Valid)
{
    ConfigManager mgr;
    std::string tempSection = "";
    mgr.ParseSection("file.conf", "[section2]", 1, tempSection);
    EXPECT_EQ(tempSection, "section2");
    EXPECT_TRUE(mgr.configMap.find("section2") != mgr.configMap.end());
}

TEST_F(TestConfigManager, GetConf_SectionNotExist)
{
    ConfigManager mgr;
    std::string val;
    EXPECT_EQ(mgr.GetConf("not_exist", "key", val), ConfigCode::SECTION_NOT_EXIST);
}

TEST_F(TestConfigManager, GetConf_KeyNotExist)
{
    ConfigManager mgr;
    mgr.configMap["sec1"] = {};
    std::string val;
    EXPECT_EQ(mgr.GetConf("sec1", "not_exist", val), ConfigCode::CONFIG_KEY_NOT_EXIST);
}

TEST_F(TestConfigManager, ReadConfFile_Success)
{
    std::string testFile = "test_read.conf";
    std::ofstream out(testFile);
    out << "[sec1]\nkey1=val1\n# comment\nkey2=val2\n";
    out.close();

    ConfigManager mgr;
    EXPECT_EQ(mgr.ReadConfFile(testFile), ConfigCode::OK);

    std::string val;
    EXPECT_EQ(mgr.GetConf("sec1", "key1", val), ConfigCode::OK);
    EXPECT_EQ(val, "val1");
    EXPECT_EQ(mgr.GetConf("sec1", "key2", val), ConfigCode::OK);
    EXPECT_EQ(val, "val2");

    std::filesystem::remove(testFile);
}

TEST_F(TestConfigManager, TravelDepthLimitedFiles_Success)
{
    std::filesystem::create_directory("test_conf_dir");
    std::ofstream out("test_conf_dir/test.conf");
    out << "[sec1]\nkey=val\n";
    out.close();

    std::vector<std::string> paths;
    EXPECT_EQ(TravelDepthLimitedFiles(paths, "test_conf_dir", 0), ConfigCode::OK);
    EXPECT_EQ(paths.size(), 1u);

    std::filesystem::remove_all("test_conf_dir");
}

TEST_F(TestConfigManager, ParseFile_CanonicalFail)
{
    ConfigManager mgr;
    EXPECT_EQ(mgr.ParseFile("/nonexistent/path/file.conf"), ConfigCode::CONFIG_FILE_READ_ERROR);
}

TEST_F(TestConfigManager, TravelDepthLimitedFiles_DepthExceeded)
{
    std::vector<std::string> paths;
    EXPECT_EQ(TravelDepthLimitedFiles(paths, ".", CONFIG_DIR_MAX_DEPTH + 1), ConfigCode::CONFIG_FOLDER_MAX_DEPTH);
}

TEST_F(TestConfigManager, TravelDepthLimitedFiles_OpenDirFail)
{
    std::vector<std::string> paths;
    EXPECT_EQ(TravelDepthLimitedFiles(paths, "/nonexistent_dir_xyz", 0), ConfigCode::CONFIG_FOLDER_OPEN_ERROR);
}

TEST_F(TestConfigManager, TravelDepthLimitedFiles_SubDirRecurse)
{
    std::filesystem::create_directories("test_conf_dir/sub");
    std::ofstream("test_conf_dir/test.conf").close();
    std::ofstream("test_conf_dir/sub/nested.conf").close();
    std::ofstream("test_conf_dir/sub/notconf.txt").close();

    std::vector<std::string> paths;
    EXPECT_EQ(TravelDepthLimitedFiles(paths, "test_conf_dir", 0), ConfigCode::OK);
    EXPECT_EQ(paths.size(), 2u);

    std::filesystem::remove_all("test_conf_dir");
}

TEST_F(TestConfigManager, ParseSection_TooShort)
{
    ConfigManager mgr;
    std::string tempSection = "old";
    mgr.ParseSection("file.conf", "[]", 1, tempSection);
    EXPECT_EQ(tempSection, "old");
}

TEST_F(TestConfigManager, ParseSection_IllegalChars)
{
    ConfigManager mgr;
    std::string tempSection = "old";
    mgr.ParseSection("file.conf", "[sec tion]", 1, tempSection);
    EXPECT_EQ(tempSection, "old");
}

TEST_F(TestConfigManager, ParseConf_KeyTooLong)
{
    ConfigManager mgr;
    std::string tempSection = "sec";
    std::string longKey(CONFIG_KEY_MAX_FIELD_LENGTH + 1, 'k');
    std::string line = longKey + "=val";
    mgr.ParseConf("file.conf", line, 1, tempSection);
    std::string val;
    EXPECT_NE(mgr.GetConf("sec", longKey, val), ConfigCode::OK);
}
TEST_F(TestConfigManager, ParseConf_ValueTooLong)
{
    ConfigManager mgr;
    std::string tempSection = "sec";
    mgr.configMap["sec"];
    std::string longVal(CONFIG_VALUE_MAX_FIELD_LENGTH + 1, 'v');
    std::string line = "key=" + longVal;
    mgr.ParseConf("file.conf", line, 1, tempSection);
    std::string val;
    EXPECT_EQ(mgr.GetConf("sec", "key", val), ConfigCode::CONFIG_KEY_NOT_EXIST);
}

TEST_F(TestConfigManager, ParseConf_KeyIllegalChars)
{
    ConfigManager mgr;
    std::string tempSection = "sec";
    mgr.configMap["sec"];
    mgr.ParseConf("file.conf", "key name=val", 1, tempSection);
    std::string val;
    EXPECT_EQ(mgr.GetConf("sec", "key name", val), ConfigCode::CONFIG_KEY_NOT_EXIST);
}

TEST_F(TestConfigManager, ParseConf_ValueIllegalChars)
{
    ConfigManager mgr;
    std::string tempSection = "sec";
    mgr.configMap["sec"];
    mgr.ParseConf("file.conf", "key=val ue", 1, tempSection);
    std::string val;
    EXPECT_EQ(mgr.GetConf("sec", "key", val), ConfigCode::CONFIG_KEY_NOT_EXIST);
}

TEST_F(TestConfigManager, ParseConf_DuplicateKey)
{
    ConfigManager mgr;
    std::string tempSection = "sec";
    mgr.ParseConf("file.conf", "key=val1", 1, tempSection);
    mgr.ParseConf("file.conf", "key=val2", 1, tempSection);
    std::string val;
    EXPECT_EQ(mgr.GetConf("sec", "key", val), ConfigCode::OK);
    EXPECT_EQ(val, "val1");
}

TEST_F(TestConfigManager, ParseLine_InvalidContent)
{
    ConfigManager mgr;
    std::string tempSection = "sec";
    mgr.configMap["sec"];
    mgr.ParseLine("file.conf", "invalid_line_without_equal", 1, tempSection);
}

TEST_F(TestConfigManager, ReadConfFile_MaxLineExceeded)
{
    std::string testFile = "test_maxline.conf";
    std::ofstream out(testFile);
    out << "[sec]\n";
    for (size_t i = 0; i < CONFIG_MAX_LINES; ++i) {
        out << "k" << i << "=v" << i << "\n";
    }
    out.close();

    ConfigManager mgr;
    EXPECT_EQ(mgr.ReadConfFile(testFile), ConfigCode::OK);
    std::filesystem::remove(testFile);
}

TEST_F(TestConfigManager, CheckParamValidation_SectionInvalid)
{
    std::string dummy;
    EXPECT_EQ(CheckParamValidation("", "key", dummy), ConfigCode::SECTION_LENGTH_INVALID);
    std::string longSec(CONFIG_SECTION_MAX_FIELD_LENGTH + 1, 's');
    EXPECT_EQ(CheckParamValidation(longSec, "key", dummy), ConfigCode::SECTION_LENGTH_INVALID);
}

TEST_F(TestConfigManager, CheckParamValidation_KeyInvalid)
{
    std::string dummy;
    EXPECT_EQ(CheckParamValidation("sec", "", dummy), ConfigCode::KEY_LENGTH_INVALID);
    std::string longKey(CONFIG_KEY_MAX_FIELD_LENGTH + 1, 'k');
    EXPECT_EQ(CheckParamValidation("sec", longKey, dummy), ConfigCode::KEY_LENGTH_INVALID);
}

TEST_F(TestConfigManager, CheckParamValidation_ValueInvalid)
{
    std::string emptyVal;
    EXPECT_EQ(CheckParamValidation("sec", "key", emptyVal, true), ConfigCode::VALUE_LENGTH_INVALID);
    std::string longVal(CONFIG_VALUE_MAX_FIELD_LENGTH + 1, 'v');
    EXPECT_EQ(CheckParamValidation("sec", "key", longVal, true), ConfigCode::VALUE_LENGTH_INVALID);
}

TEST_F(TestConfigManager, CheckParamValidation_OK)
{
    std::string val = "valid";
    EXPECT_EQ(CheckParamValidation("sec", "key", val, true), ConfigCode::OK);
    EXPECT_EQ(CheckParamValidation("sec", "key", val), ConfigCode::OK);
}

TEST_F(TestConfigManager, CheckNoIllegalChars_ValueMode)
{
    EXPECT_TRUE(CheckNoIllegalChars("valid_val:1/2,3;4-5", true));
    EXPECT_FALSE(CheckNoIllegalChars("invalid value!", true));
    EXPECT_TRUE(CheckNoIllegalChars("", true));
}

TEST_F(TestConfigManager, FormatErrorMessage_AllFields)
{
    std::string msg = FormatErrorMessage("Test error", 42, "section1", "key1", "value1");
    EXPECT_NE(msg.find("Test error"), std::string::npos);
    EXPECT_NE(msg.find("42"), std::string::npos);
    EXPECT_NE(msg.find("section1"), std::string::npos);
    EXPECT_NE(msg.find("key1"), std::string::npos);
    EXPECT_NE(msg.find("value1"), std::string::npos);
}

TEST_F(TestConfigManager, Init_TravelDepthFailed)
{
    ConfigManager mgr;
    auto ret = mgr.Init("/nonexistent_dir_xyz");
    EXPECT_EQ(ret, ConfigCode::CONFIG_FOLDER_OPEN_ERROR);
}

TEST_F(TestConfigManager, Init_PrefixNotMatch)
{
    std::filesystem::create_directory("test_prefix_dir");
    std::ofstream("test_prefix_dir/app.conf").close();
    std::ofstream("test_prefix_dir/not_match.txt").close();

    ConfigManager mgr;
    auto ret = mgr.Init("test_prefix_dir", "nomatch");
    EXPECT_EQ(ret, ConfigCode::OK);

    std::filesystem::remove_all("test_prefix_dir");
}

TEST_F(TestConfigManager, Init_WithPrefixFilter)
{
    std::filesystem::create_directory("test_filter_dir");
    std::ofstream out("test_filter_dir/ubs.conf");
    out << "[sec]\nkey=val\n";
    out.close();
    std::ofstream("test_filter_dir/other.conf").close();

    ConfigManager mgr;
    auto ret = mgr.Init("test_filter_dir", "ubs");
    EXPECT_EQ(ret, ConfigCode::OK);
    std::string val;
    EXPECT_EQ(mgr.GetConf("sec", "key", val), ConfigCode::OK);
    EXPECT_EQ(val, "val");

    std::filesystem::remove_all("test_filter_dir");
}

TEST_F(TestConfigManager, Init_FileAlreadyLoaded)
{
    std::filesystem::create_directory("test_reload_dir");
    std::ofstream out("test_reload_dir/test.conf");
    out << "[sec]\nkey=val\n";
    out.close();

    ConfigManager mgr;
    EXPECT_EQ(mgr.Init("test_reload_dir"), ConfigCode::OK);
    EXPECT_EQ(mgr.Init("test_reload_dir"), ConfigCode::OK);

    std::filesystem::remove_all("test_reload_dir");
}

TEST_F(TestConfigManager, ReadConfFile_FileOpenFailed)
{
    ConfigManager mgr;
    EXPECT_EQ(mgr.ReadConfFile("/nonexistent_dir/test.conf"), ConfigCode::CONFIG_FILE_READ_ERROR);
}

TEST_F(TestConfigManager, TravelDepthLimitedFiles_LstatFail)
{
    std::filesystem::create_directory("test_lstat_dir");
    std::ofstream("test_lstat_dir/good.conf").close();

    std::vector<std::string> paths;
    MOCKER(lstat).stubs().will(returnValue(-1));
    auto ret = TravelDepthLimitedFiles(paths, "test_lstat_dir", 0);
    EXPECT_EQ(ret, ConfigCode::OK);
    MOCKER(lstat).reset();

    std::filesystem::remove_all("test_lstat_dir");
}

TEST_F(TestConfigManager, ParseLine_CommentSemicolon)
{
    ConfigManager mgr;
    std::string tempSection = "sec";
    mgr.configMap["sec"];
    mgr.ParseLine("file.conf", "; this is a comment", 1, tempSection);
    SUCCEED();
}

TEST_F(TestConfigManager, ParseLine_CommentHash)
{
    ConfigManager mgr;
    std::string tempSection = "sec";
    mgr.configMap["sec"];
    mgr.ParseLine("file.conf", "# this is a comment", 1, tempSection);
    SUCCEED();
}

TEST_F(TestConfigManager, ParseLine_EmptyLine)
{
    ConfigManager mgr;
    std::string tempSection = "sec";
    mgr.configMap["sec"];
    mgr.ParseLine("file.conf", "", 1, tempSection);
    SUCCEED();
}

TEST_F(TestConfigManager, ParseLine_WhitespaceOnly)
{
    ConfigManager mgr;
    std::string tempSection = "sec";
    mgr.configMap["sec"];
    mgr.ParseLine("file.conf", "   ", 1, tempSection);
    SUCCEED();
}

TEST_F(TestConfigManager, ParseSection_ExistingSection)
{
    ConfigManager mgr;
    std::string tempSection = "old";
    mgr.configMap["existing"];
    mgr.ParseSection("file.conf", "[existing]", 1, tempSection);
    EXPECT_EQ(tempSection, "existing");
}

TEST_F(TestConfigManager, CatString_SingleElement)
{
    std::vector<std::string> vec = {"single"};
    EXPECT_EQ(CatString(vec, ","), "single");
}

TEST_F(TestConfigManager, ParseLine_MaxLineExceeded)
{
    ConfigManager mgr;
    std::string tempSection = "sec";
    mgr.ParseLine("file.conf", "key=val", CONFIG_MAX_LINES + 2, tempSection);
    EXPECT_FALSE(mgr.parseErrors.empty());
}

TEST_F(TestConfigManager, CheckNoIllegalChars_NonValChars)
{
    EXPECT_FALSE(CheckNoIllegalChars("key with space"));
    EXPECT_FALSE(CheckNoIllegalChars("key@invalid"));
}

TEST_F(TestConfigManager, Init_ParseErrorStored)
{
    std::filesystem::create_directory("test_parse_err_dir");
    std::ofstream out("test_parse_err_dir/bad.conf");
    out << "[ok_section]\n=no_key\n";
    out.close();

    ConfigManager mgr;
    mgr.Init("test_parse_err_dir");

    std::filesystem::remove_all("test_parse_err_dir");
}

TEST_F(TestConfigManager, ParseConf_KeyTooShort)
{
    ConfigManager mgr;
    std::string tempSection = "sec";
    mgr.configMap["sec"];
    mgr.ParseConf("file.conf", "  =val", 1, tempSection);
    std::string val;
    EXPECT_NE(mgr.GetConf("sec", "", val), ConfigCode::OK);
}

TEST_F(TestConfigManager, ParseConf_SectionAutoCreate)
{
    ConfigManager mgr;
    std::string tempSection = "auto_sec";
    mgr.ParseConf("file.conf", "key=val", 1, tempSection);
    std::string val;
    EXPECT_EQ(mgr.GetConf("auto_sec", "key", val), ConfigCode::OK);
    EXPECT_EQ(val, "val");
}

TEST_F(TestConfigManager, Format_CustomDelimiters)
{
    Format fmt("((", "))", ":", "//", "--");
    EXPECT_TRUE(fmt.IsSectionStart("(("));
    EXPECT_TRUE(fmt.IsSectionEnd("))"));
    EXPECT_TRUE(fmt.IsAssign(":"));
    EXPECT_TRUE(fmt.IsComment("//"));
    EXPECT_TRUE(fmt.IsComment("--"));
}

TEST_F(TestConfigManager, TravelDepthLimitedFiles_DirWithInvalidEntry)
{
    std::filesystem::create_directory("test_mixed_dir");
    std::ofstream("test_mixed_dir/valid.conf").close();
    std::ofstream("test_mixed_dir/notconf.txt").close();

    std::vector<std::string> paths;
    auto ret = TravelDepthLimitedFiles(paths, "test_mixed_dir", 0);
    EXPECT_EQ(ret, ConfigCode::OK);
    EXPECT_EQ(paths.size(), 1u);

    std::filesystem::remove_all("test_mixed_dir");
}

TEST_F(TestConfigManager, GetConf_FromMultipleSections)
{
    ConfigManager mgr;
    std::string sec1 = "s1";
    std::string sec2 = "s2";
    mgr.configMap[sec1]["k"] = "v1";
    mgr.configMap[sec2]["k"] = "v2";

    std::string val;
    EXPECT_EQ(mgr.GetConf("s1", "k", val), ConfigCode::OK);
    EXPECT_EQ(val, "v1");
    EXPECT_EQ(mgr.GetConf("s2", "k", val), ConfigCode::OK);
    EXPECT_EQ(val, "v2");
}

} // namespace ovs::ut