/********************************************************************************
 * Copyright (c) 2025-2026 ZF Friedrichshafen AG
 *
 * This program and the accompanying materials are made available under the
 * terms of the Apache License Version 2.0 which is available at
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Contributors:
 *   Erik Verhoeven - initial API and implementation
 ********************************************************************************/

#include <gtest/gtest.h>
#include <support/sdv_core.h>
#include <interfaces/app.h>
#include <support/mem_access.h>
#include <support/app_control.h>
#include <filesystem>
#include "../../../global/exec_dir_helper.h"
#include "../../../global/base64.h"

#ifdef _WIN32
#include <Shlobj.h>
#endif

class CModuleControl_Install : public ::testing::Test
{
public:
    virtual void SetUp()
    {
        DeleteTestDirs();
    }

    virtual void TearDown()
    {
        DeleteTestDirs();
    }

    static void SetUpTestCase() {}
    static void TearDownTestSuite() {}

    void DeleteTestDirs()
    {
        try
        {
            std::filesystem::remove_all(GetExecDirectory() / "module_test1");
        }
        catch (const std::filesystem::filesystem_error&)
        {}
        try
        {
            std::filesystem::remove_all(GetExecDirectory() / "module_test2");
        }
        catch (const std::filesystem::filesystem_error&)
        {}
        try
        {
            std::filesystem::remove_all(GetDefaultInstallDir());
        }
        catch (const std::filesystem::filesystem_error&)
        {}
    }

    /**
     * @brief Default installation directory.
     * @return The default installation directory.
     */
    std::filesystem::path GetDefaultInstallDir();
};

std::filesystem::path CModuleControl_Install::GetDefaultInstallDir()
{
    // Append the installation directory with /<instance_ID>/.
    return (GetExecDirectory() / "../../bin/2006").lexically_normal();
}

TEST_F(CModuleControl_Install, MainApp_DefaultInstallDir)
{
    sdv::app::CAppControl control;
    bool bResult = control.Startup(R"toml([Application]
Mode = "Main"
Instance = 2006

[Console]
RedirectMon = true
)toml");
    EXPECT_TRUE(bResult);

    // The default installation directory should be created.
    EXPECT_TRUE(std::filesystem::exists(control.GetComponentInstallDirectory()));

    control.Shutdown();
}

TEST_F(CModuleControl_Install, IsolatedApp_DefaultInstallDir)
{
    sdv::app::CAppControl control;
    std::string ssConnectionString = Base64EncodePlainText("");
    std::string ssConfig = R"toml([Application]
Mode = "Isolated"
Instance = 2006
Connection = ")toml";
    ssConfig += Base64EncodePlainText("test") + "\"";
    bool bResult = control.Startup(ssConfig);
    EXPECT_TRUE(bResult);

    // The default installation directory should be created.
    EXPECT_TRUE(std::filesystem::exists(GetDefaultInstallDir()));

    control.Shutdown();
}

TEST_F(CModuleControl_Install, OtherApp_DefaultInstallDir)
{
    sdv::app::CAppControl control;
    bool bResult = control.Startup("");
    EXPECT_TRUE(bResult);

    // The default installation directory should be created.
    EXPECT_FALSE(std::filesystem::exists(GetDefaultInstallDir()));

    control.Shutdown();
}

TEST_F(CModuleControl_Install, MainApp_BlockDefaultInstallDir)
{
    // Prevent error reporting on std::cerr - they will influence test outcome.
    auto* pCErr = std::cerr.rdbuf();
    std::ostringstream sstreamCErr;
    std::cerr.rdbuf(sstreamCErr.rdbuf());

    // Create a blocking file... cannot create directory!
    std::ofstream fstream(GetDefaultInstallDir());
    ASSERT_TRUE(fstream.is_open());
    fstream << "Dummy string...";
    fstream.close();

    sdv::app::CAppControl control;
    bool bResult = control.Startup(R"toml([Application]
Mode = "Main"
Instance = 2006

[Console]
RedirectMon = true
)toml");
    EXPECT_FALSE(bResult);

    // The default installation directory should be created.
    EXPECT_TRUE(std::filesystem::exists(GetDefaultInstallDir()));

    std::cerr.rdbuf(pCErr);
}

TEST_F(CModuleControl_Install, IsolatedApp_BlockDefaultInstallDir)
{
    // Prevent error reporting on std::cerr - they will influence test outcome.
    auto* pCErr = std::cerr.rdbuf();
    std::ostringstream sstreamCErr;
    std::cerr.rdbuf(sstreamCErr.rdbuf());

    std::ofstream fstream(GetDefaultInstallDir());
    ASSERT_TRUE(fstream.is_open());
    fstream << "Dummy string...";
    fstream.close();

    sdv::app::CAppControl control;
    bool bResult = control.Startup(R"toml([Application]
Mode = "Isolated"
Instance = 2006)toml");
    EXPECT_FALSE(bResult);

    // The default installation directory should be created.
    EXPECT_TRUE(std::filesystem::exists(GetDefaultInstallDir()));

    std::cerr.rdbuf(pCErr);
}

TEST_F(CModuleControl_Install, OtherApp_BlockDefaultInstallDir)
{
    std::ofstream fstream(GetDefaultInstallDir());
    ASSERT_TRUE(fstream.is_open());
    fstream << "Dummy string...";
    fstream.close();

    sdv::app::CAppControl control;
    bool bResult = control.Startup("");
    EXPECT_TRUE(bResult);

    // The default installation directory should be created.
    EXPECT_TRUE(std::filesystem::exists(GetDefaultInstallDir()));

    control.Shutdown();
}

TEST_F(CModuleControl_Install, MainApp_CustomInstallDirRelative)
{
    sdv::app::CAppControl control;
    bool bResult = control.Startup(R"toml(
[Application]
Mode="Main"
Instance=2006
InstallDir = "module_test1"
)toml");
    EXPECT_TRUE(bResult);

    // The default installation directory should be created.
    EXPECT_TRUE(std::filesystem::exists(GetExecDirectory() / "module_test1"));

    control.Shutdown();
}

TEST_F(CModuleControl_Install, IsolatedApp_CustomInstallDirRelative)
{
    sdv::app::CAppControl control;
    bool bResult = control.Startup(std::string(R"toml(
[Application]
Mode="Isolated"
Instance=2006
InstallDir = "module_test1"
Connection = ")toml") + Base64EncodePlainText("test") + "\"");
    EXPECT_TRUE(bResult);

    // The default installation directory should be created.
    EXPECT_TRUE(std::filesystem::exists(GetExecDirectory() / "module_test1"));

    control.Shutdown();
}

TEST_F(CModuleControl_Install, OtherApp_CustomInstallDirRelative)
{
    sdv::app::CAppControl control;
    bool bResult = control.Startup(R"toml([Application]
Install.Dir = "module_test1"
)toml");
    EXPECT_TRUE(bResult);

    // The default installation directory should be created.
    EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "module_test1"));

    control.Shutdown();
}

TEST_F(CModuleControl_Install, MainApp_CustomInstallDirAbsolute)
{
    sdv::app::CAppControl control;
    bool bResult = control.Startup(std::string(R"toml(
[Application]
Mode="Main"
Instance=2006
InstallDir = ")toml") + (GetExecDirectory() / "module_test2").generic_u8string() + "\"");
    EXPECT_TRUE(bResult);

    // The default installation directory should be created.
    EXPECT_TRUE(std::filesystem::exists(GetExecDirectory() / "module_test2"));

    control.Shutdown();
}

TEST_F(CModuleControl_Install, IsolatedApp_CustomInstallDirAbsolute)
{
    sdv::app::CAppControl control;
    bool bResult = control.Startup(std::string(R"toml(
[Application]
Mode="Isolated"
Instance=2006
InstallDir = ")toml") + (GetExecDirectory() / "module_test2").generic_u8string() + "\"" + R"toml(
Connection = ")toml" + Base64EncodePlainText("test") + "\"");
    EXPECT_TRUE(bResult);

    // The default installation directory should be created.
    EXPECT_TRUE(std::filesystem::exists(GetExecDirectory() / "module_test2"));

    control.Shutdown();
}

TEST_F(CModuleControl_Install, OtherApp_CustomInstallDirAbsolute)
{
    sdv::app::CAppControl control;
    bool bResult = control.Startup(std::string(R"toml([Application]
InstallDir = ")toml") + (GetExecDirectory() / "module_test2").generic_u8string() + "\"");
    EXPECT_TRUE(bResult);

    // The default installation directory should be created.
    EXPECT_FALSE(std::filesystem::exists(GetExecDirectory() / "module_test2"));

    control.Shutdown();
}

