#include "Process.hpp"
#include "PathUtils.hpp"

#include <shellapi.h>
#include <iostream>
#include <vector>

namespace GModTeach {
namespace Platform {

namespace {

struct ScopedHandle {
    HANDLE handle = INVALID_HANDLE_VALUE;
    ScopedHandle(HANDLE h) : handle(h) {}
    ~ScopedHandle() {
        if (handle != INVALID_HANDLE_VALUE && handle != nullptr) {
            CloseHandle(handle);
        }
    }
    operator HANDLE() const { return handle; }
};

} 

ProcessResult Process::Execute(
    const std::filesystem::path& executable,
    const std::wstring& arguments,
    bool hidden,
    DWORD timeoutMs
) {
    std::wstring cmdLine = L"\"" + executable.wstring() + L"\" " + arguments;
    return ExecuteCommandLine(cmdLine, hidden, timeoutMs);
}

ProcessResult Process::ExecuteCommandLine(
    const std::wstring& commandLine,
    bool hidden,
    DWORD timeoutMs
) {
    ProcessResult result;

    std::vector<wchar_t> cmdBuffer(commandLine.begin(), commandLine.end());
    cmdBuffer.push_back(L'\0');

    STARTUPINFOW si{};
    PROCESS_INFORMATION pi{};
    si.cb = sizeof(si);

    DWORD flags = 0;
    if (hidden) {
        si.dwFlags |= STARTF_USESHOWWINDOW;
        si.wShowWindow = SW_HIDE;
        flags |= CREATE_NO_WINDOW;
    }

    const BOOL success = CreateProcessW(
        nullptr,
        cmdBuffer.data(),
        nullptr,
        nullptr,
        FALSE,
        flags,
        nullptr,
        nullptr,
        &si,
        &pi
    );

    if (!success) {
        std::wcerr << L"[ERROR] Failed to execute process: " << commandLine 
                   << L" (Win32 Error: " << GetLastError() << L")\n";
        return result;
    }

    ScopedHandle processHandle(pi.hProcess);
    ScopedHandle threadHandle(pi.hThread);

    result.executed = true;
    WaitForSingleObject(processHandle, timeoutMs);
    GetExitCodeProcess(processHandle, &result.exitCode);

    return result;
}

void Process::OpenBrowserURL(const std::string& url) {
    ShellExecuteA(nullptr, "open", url.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
}

void Process::RestartSelf() {
    const auto exePath = PathUtils::GetExecutablePath();
    ShellExecuteW(nullptr, L"open", exePath.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
    ExitProcess(0);
}

} 
} 
