#include "pch.h"
#include "TestHelpers.h"
#include <processApi.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace UnitTestsCommonUtils
{
    TEST_CLASS(ProcessApiTests)
    {
    public:
        TEST_METHOD(GetProcessHandlesByName_CurrentProcess_ReturnsHandles)
        {
            // Get current process executable name
            wchar_t path[MAX_PATH];
            GetModuleFileNameW(nullptr, path, MAX_PATH);

            // Extract just the filename
            std::wstring fullPath(path);
            auto lastSlash = fullPath.rfind(L'\\');
            std::wstring exeName = (lastSlash != std::wstring::npos) ?
                fullPath.substr(lastSlash + 1) : fullPath;

            auto handles = getProcessHandlesByName(exeName, PROCESS_QUERY_LIMITED_INFORMATION);

            // Should find at least our own process
            Assert::IsFalse(handles.empty());

            // Handles are RAII-managed
        }

        TEST_METHOD(GetProcessHandlesByName_NonExistentProcess_ReturnsEmpty)
        {
            auto handles = getProcessHandlesByName(L"NonExistentProcess12345.exe", PROCESS_QUERY_LIMITED_INFORMATION);
            Assert::IsTrue(handles.empty());
        }

        TEST_METHOD(GetProcessHandlesByName_EmptyName_ReturnsEmpty)
        {
            auto handles = getProcessHandlesByName(L"", PROCESS_QUERY_LIMITED_INFORMATION);
            Assert::IsTrue(handles.empty());
        }

        TEST_METHOD(GetProcessHandlesByName_Explorer_ReturnsHandles)
        {
            // Explorer.exe should typically be running
            auto handles = getProcessHandlesByName(L"explorer.exe", PROCESS_QUERY_LIMITED_INFORMATION);

            // Handles are RAII-managed

            // May or may not find explorer depending on system state
            // Just verify it doesn't crash
            Assert::IsTrue(true);
        }

        TEST_METHOD(GetProcessHandlesByName_CaseInsensitive_Works)
        {
            // Get current process name in uppercase
            wchar_t path[MAX_PATH];
            GetModuleFileNameW(nullptr, path, MAX_PATH);

            std::wstring fullPath(path);
            auto lastSlash = fullPath.rfind(L'\\');
            std::wstring exeName = (lastSlash != std::wstring::npos) ?
                fullPath.substr(lastSlash + 1) : fullPath;

            // Convert to uppercase
            std::wstring upperName = exeName;
            std::transform(upperName.begin(), upperName.end(), upperName.begin(), ::towupper);

            auto handles = getProcessHandlesByName(upperName, PROCESS_QUERY_LIMITED_INFORMATION);

            // Handles are RAII-managed

            // The function may or may not be case insensitive - just don't crash
            Assert::IsTrue(true);
        }

        TEST_METHOD(GetProcessHandlesByName_DifferentAccessRights_Works)
        {
            wchar_t path[MAX_PATH];
            GetModuleFileNameW(nullptr, path, MAX_PATH);

            std::wstring fullPath(path);
            auto lastSlash = fullPath.rfind(L'\\');
            std::wstring exeName = (lastSlash != std::wstring::npos) ?
                fullPath.substr(lastSlash + 1) : fullPath;

            // Try with different access rights
            auto handles1 = getProcessHandlesByName(exeName, PROCESS_QUERY_INFORMATION);
            auto handles2 = getProcessHandlesByName(exeName, PROCESS_VM_READ);

            // Handles are RAII-managed

            // Just verify no crashes
            Assert::IsTrue(true);
        }

        TEST_METHOD(GetProcessHandlesByName_SystemProcess_MayRequireElevation)
        {
            // System processes might require elevation
            auto handles = getProcessHandlesByName(L"System", PROCESS_QUERY_LIMITED_INFORMATION);

            // Handles are RAII-managed

            // Just verify no crashes
            Assert::IsTrue(true);
        }

        TEST_METHOD(GetProcessHandlesByName_ValidHandles_AreUsable)
        {
            wchar_t path[MAX_PATH];
            GetModuleFileNameW(nullptr, path, MAX_PATH);

            std::wstring fullPath(path);
            auto lastSlash = fullPath.rfind(L'\\');
            std::wstring exeName = (lastSlash != std::wstring::npos) ?
                fullPath.substr(lastSlash + 1) : fullPath;

            auto handles = getProcessHandlesByName(exeName, PROCESS_QUERY_LIMITED_INFORMATION);

            bool foundValidHandle = false;
            for (auto& handle : handles)
            {
                // Try to use the handle
                DWORD exitCode;
                if (GetExitCodeProcess(handle.get(), &exitCode))
                {
                    foundValidHandle = true;
                }
            }

            Assert::IsTrue(foundValidHandle || handles.empty());
        }
    };
}
