#include "ProcessActions.h"

#include "../../../Ksword5.1/Ksword5.1/ArkDriverClient/ArkDriverClient.h"
#include "../../../Ksword5.1/Ksword5.1/ksword/process/process.h"
#include "../../Core\Common.h"

#include <algorithm>
#include <cstring>
#include <cstdint>
#include <fstream>
#include <functional>
#include <iomanip>
#include <shellapi.h>
#include <sstream>
#include <string>
#include <tlhelp32.h>
#include <utility>
#include <unordered_map>
#include <unordered_set>

#ifndef SECURITY_MANDATORY_MEDIUM_PLUS_RID
#define SECURITY_MANDATORY_MEDIUM_PLUS_RID (0x00002100L)
#endif

namespace Ksword::Features::Process {
namespace {
constexpr ULONG kProcessBreakOnTerminationInfoClass = 29UL;
constexpr ULONG kProcessPowerThrottlingInfoClass = 4UL;
constexpr ULONG kProcessPowerThrottlingCurrentVersion = 1UL;
constexpr ULONG kProcessPowerThrottlingExecutionSpeed = 0x1UL;
constexpr DWORD kProcessSuspendResumeAccess = 0x0800UL;

using NtSuspendProcessFn = LONG(NTAPI*)(HANDLE);
using NtResumeProcessFn = LONG(NTAPI*)(HANDLE);
using NtSetInformationProcessFn = LONG(NTAPI*)(HANDLE, ULONG, PVOID, ULONG);
using SetProcessInformationFn = BOOL(WINAPI*)(HANDLE, ULONG, LPVOID, DWORD);

// ProcessPowerThrottlingStateNative mirrors PROCESS_POWER_THROTTLING_STATE
// without requiring a new SDK. Inputs are written by SetEfficiencyModeForPid;
// processing passes the structure to SetProcessInformation; it returns no value.
struct ProcessPowerThrottlingStateNative {
    ULONG version = 0;
    ULONG controlMask = 0;
    ULONG stateMask = 0;
};

// Utf8ToWide converts ArkDriverClient diagnostic messages into the Win32 UI
// encoding. Input is a UTF-8/narrow diagnostic string; processing asks Windows
// for the exact UTF-16 size and falls back to byte widening when conversion is
// impossible; output is safe for status text and message boxes.
std::wstring Utf8ToWide(const std::string& text) {
    if (text.empty()) {
        return {};
    }

    const int required = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text.c_str(), static_cast<int>(text.size()), nullptr, 0);
    if (required > 0) {
        std::wstring wide(static_cast<std::size_t>(required), L'\0');
        ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text.c_str(), static_cast<int>(text.size()), wide.data(), required);
        return wide;
    }

    std::wstring fallback;
    fallback.reserve(text.size());
    for (const unsigned char ch : text) {
        fallback.push_back(static_cast<wchar_t>(ch));
    }
    return fallback;
}

std::wstring PidListText(const std::vector<DWORD>& pids) {
    std::wstring text;
    for (std::size_t i = 0; i < pids.size(); ++i) {
        if (i != 0) {
            text += L", ";
        }
        text += std::to_wstring(pids[i]);
    }
    return text.empty() ? L"<none>" : text;
}

// WriteClipboardText copies Unicode operation handoff text to the clipboard.
// Input is owner HWND (optional) and text; processing transfers GMEM_MOVEABLE
// memory to the OS clipboard; output reports whether the copy succeeded.
bool WriteClipboardText(HWND owner, const std::wstring& text) {
    if (!::OpenClipboard(owner)) {
        return false;
    }
    ::EmptyClipboard();
    const SIZE_T bytes = (text.size() + 1) * sizeof(wchar_t);
    HGLOBAL memory = ::GlobalAlloc(GMEM_MOVEABLE, bytes);
    if (!memory) {
        ::CloseClipboard();
        return false;
    }
    void* target = ::GlobalLock(memory);
    if (!target) {
        ::GlobalFree(memory);
        ::CloseClipboard();
        return false;
    }
    std::memcpy(target, text.c_str(), bytes);
    ::GlobalUnlock(memory);
    const bool ok = ::SetClipboardData(CF_UNICODETEXT, memory) != nullptr;
    if (!ok) {
        ::GlobalFree(memory);
    }
    ::CloseClipboard();
    return ok;
}

const ProcessSnapshotRow* FindRowByPid(const std::vector<ProcessSnapshotRow>& rows, DWORD pid) {
    const auto it = std::find_if(rows.begin(), rows.end(), [pid](const ProcessSnapshotRow& row) {
        return row.processId == pid;
    });
    return it == rows.end() ? nullptr : &*it;
}

// BuildProcessActionTargets preserves the exact process instances selected by
// the user. Missing rows remain explicit zero-identity targets so mutations fail
// closed instead of falling back to whatever later owns the same PID.
std::vector<ProcessSnapshotRow> BuildProcessActionTargets(
    const std::vector<DWORD>& selectedPids,
    const std::vector<ProcessSnapshotRow>& snapshotRows) {
    std::vector<ProcessSnapshotRow> targets;
    targets.reserve(selectedPids.size());
    std::unordered_set<DWORD> visitedPids;
    visitedPids.reserve(selectedPids.size());
    for (const DWORD pid : selectedPids) {
        if (pid == 0U || !visitedPids.insert(pid).second) {
            continue;
        }
        const ProcessSnapshotRow* row = FindRowByPid(snapshotRows, pid);
        if (row != nullptr) {
            targets.push_back(*row);
            continue;
        }
        ProcessSnapshotRow missingTarget{};
        missingTarget.processId = pid;
        targets.push_back(std::move(missingTarget));
    }
    return targets;
}

// CollectR3ProcessTreePids expands the selected R3 processes into descendant-
// first termination targets. R0-only audit rows are deliberately excluded from
// both roots and descendants, so driver evidence never changes tree discovery.
std::vector<DWORD> CollectR3ProcessTreePids(
    const std::vector<DWORD>& selectedPids,
    const std::vector<ProcessSnapshotRow>& snapshotRows) {
    std::unordered_map<DWORD, std::vector<DWORD>> childrenByParentPid;
    std::unordered_set<DWORD> r3PidSet;
    childrenByParentPid.reserve(snapshotRows.size());
    r3PidSet.reserve(snapshotRows.size());

    for (const ProcessSnapshotRow& row : snapshotRows) {
        if (row.r0KernelOnly || row.processId == 0U || !r3PidSet.insert(row.processId).second) {
            continue;
        }
        childrenByParentPid[row.parentProcessId].push_back(row.processId);
    }

    for (auto& childPair : childrenByParentPid) {
        std::vector<DWORD>& childPids = childPair.second;
        std::sort(childPids.begin(), childPids.end());
    }

    std::vector<DWORD> treePids;
    treePids.reserve(r3PidSet.size());
    std::unordered_set<DWORD> visitedPids;
    visitedPids.reserve(r3PidSet.size());
    std::function<void(DWORD)> appendSubtree;
    appendSubtree =
        [&childrenByParentPid, &treePids, &visitedPids, &appendSubtree](const DWORD processId) {
        if (!visitedPids.insert(processId).second) {
            return;
        }

        const auto childIt = childrenByParentPid.find(processId);
        if (childIt != childrenByParentPid.end()) {
            for (const DWORD childPid : childIt->second) {
                appendSubtree(childPid);
            }
        }
        treePids.push_back(processId);
    };

    for (const DWORD selectedPid : selectedPids) {
        if (r3PidSet.find(selectedPid) != r3PidSet.end()) {
            appendSubtree(selectedPid);
        }
    }
    return treePids;
}

ProcessActionResult FailureResult(const wchar_t* title, const std::vector<DWORD>& pids, const wchar_t* reason) {
    ProcessActionResult result;
    result.success = false;
    result.title = title;
    result.detail = std::wstring(reason) + L"\r\nTarget PID(s): " + PidListText(pids);
    return result;
}

// Win32ErrorText formats the current or supplied Win32 error. Input is the error
// code; processing delegates message formatting to Core; output is display text.
std::wstring Win32ErrorText(const DWORD error) {
    return L"Win32 " + std::to_wstring(error) + L": " + Ksword::Core::LastErrorMessage(error);
}

// Hex32 formats NTSTATUS-style signed LONG values without losing the raw bits.
// Input is an NTSTATUS-compatible value; output is uppercase 8-digit hex text.
std::wstring Hex32(const LONG status) {
    std::wostringstream stream;
    stream << L"0x" << std::uppercase << std::hex << std::setw(8) << std::setfill(L'0')
           << static_cast<std::uint32_t>(status);
    return stream.str();
}

// Hex64 formats pointer-sized diagnostics without truncating kernel/user
// addresses. Input is a 64-bit value from ArkDriverClient; output is a stable
// uppercase hexadecimal string used only for display.
std::wstring Hex64(const std::uint64_t value) {
    std::wostringstream stream;
    stream << L"0x" << std::uppercase << std::hex << value;
    return stream.str();
}

// AsciiLiteralToWide widens short export names or fixed ASCII diagnostics.
// Input is a null-terminated ASCII string; processing widens byte-for-byte;
// output is empty when input is null.
std::wstring AsciiLiteralToWide(const char* text) {
    if (!text) {
        return {};
    }
    std::wstring wide;
    while (*text) {
        wide.push_back(static_cast<wchar_t>(*text));
        ++text;
    }
    return wide;
}

// NtProc resolves one ntdll export by name. Input is an ANSI export name;
// processing uses the already-loaded ntdll module or loads it; output is null
// when the export cannot be found.
FARPROC NtProc(const char* name) {
    if (!name || name[0] == '\0') {
        return nullptr;
    }
    HMODULE ntdll = ::GetModuleHandleW(L"ntdll.dll");
    if (!ntdll) {
        ntdll = ::LoadLibraryW(L"ntdll.dll");
    }
    return ntdll ? ::GetProcAddress(ntdll, name) : nullptr;
}

// EnableCurrentProcessPrivilege enables one privilege on the current token.
// Input is a privilege name such as SE_DEBUG_NAME; processing adjusts the
// process token; output reports whether Windows accepted and assigned it.
bool EnableCurrentProcessPrivilege(const wchar_t* privilegeName, std::wstring& detail) {
    HANDLE rawToken = nullptr;
    if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &rawToken)) {
        detail = L"OpenProcessToken failed: " + Win32ErrorText(::GetLastError());
        return false;
    }
    Ksword::Core::UniqueHandle token(rawToken);

    LUID luid{};
    if (!::LookupPrivilegeValueW(nullptr, privilegeName, &luid)) {
        detail = L"LookupPrivilegeValueW failed: " + Win32ErrorText(::GetLastError());
        return false;
    }

    TOKEN_PRIVILEGES privileges{};
    privileges.PrivilegeCount = 1;
    privileges.Privileges[0].Luid = luid;
    privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    if (!::AdjustTokenPrivileges(token.get(), FALSE, &privileges, sizeof(privileges), nullptr, nullptr)) {
        detail = L"AdjustTokenPrivileges failed: " + Win32ErrorText(::GetLastError());
        return false;
    }

    const DWORD adjustError = ::GetLastError();
    if (adjustError != ERROR_SUCCESS) {
        detail = L"AdjustTokenPrivileges did not assign privilege: " + Win32ErrorText(adjustError);
        return false;
    }
    detail = std::wstring(privilegeName ? privilegeName : L"<null>") + L" enabled";
    return true;
}

// AppendIoLine records one per-PID operation result. Inputs are a mutable
// details buffer, PID, operation label, success bit and driver/Win32 message;
// processing emits compact multiline diagnostics; no value is returned.
void AppendIoLine(std::wstring& detail, DWORD pid, const wchar_t* operation, bool ok, const std::wstring& message) {
    detail += L"PID " + std::to_wstring(pid) + L" ";
    detail += operation;
    detail += ok ? L": OK" : L": FAIL";
    if (!message.empty()) {
        detail += L" | ";
        detail += message;
    }
    detail += L"\r\n";
}

// AppendIoLine records a global non-PID operation such as clearing hidden marks.
// Inputs mirror the PID overload except there is no target process id.
void AppendIoLine(std::wstring& detail, const wchar_t* operation, bool ok, const std::wstring& message) {
    detail += operation;
    detail += ok ? L": OK" : L": FAIL";
    if (!message.empty()) {
        detail += L" | ";
        detail += message;
    }
    detail += L"\r\n";
}

// IsProtectedSystemPid blocks obviously invalid targets before sending mutating
// process IOCTLs. Input is a PID; output is true for PID 0..4, matching the
// original KswordARK R0 helpers.
bool IsProtectedSystemPid(DWORD pid) {
    return pid == 0 || pid <= 4;
}

// IsProcessPresentBySnapshot checks target liveness with the same Toolhelp
// snapshot semantics as the full ProcessDock aggregate termination action.
// A failed snapshot is conservatively treated as "still present" so a
// transient query failure cannot report a process as terminated.
bool IsProcessPresentBySnapshot(DWORD pid, bool* queryOkOut) {
    if (queryOkOut) {
        *queryOkOut = false;
    }

    HANDLE snapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snapshot == INVALID_HANDLE_VALUE) {
        return true;
    }

    PROCESSENTRY32W entry{};
    entry.dwSize = sizeof(entry);
    if (!::Process32FirstW(snapshot, &entry)) {
        ::CloseHandle(snapshot);
        return true;
    }

    bool present = false;
    do {
        if (entry.th32ProcessID == pid) {
            present = true;
            break;
        }
    } while (::Process32NextW(snapshot, &entry));

    ::CloseHandle(snapshot);
    if (queryOkOut) {
        *queryOkOut = true;
    }
    return present;
}

// OpenProcessForAction retains a verified process handle for the duration of a
// mutation, so a PID cannot be silently rebound to a different snapshot row.
Ksword::Core::UniqueHandle OpenProcessForAction(
    DWORD pid,
    ULONGLONG expectedCreationTime100ns,
    DWORD access,
    std::wstring& errorText,
    bool rejectProtected = true);
// ExecuteMultiMethodTerminate mirrors the full ProcessDock right-click action:
// each target is checked after every method and the chain stops immediately
// once its exit has been confirmed. The two-round cap prevents an unresponsive
// target from leaving the Light UI in an unbounded operation.
ProcessActionResult ExecuteMultiMethodTerminate(const std::vector<ProcessSnapshotRow>& actionTargets) {
    ProcessActionResult result;
    result.title = L"结束进程(组合方法链)";
    result.success = true;

    struct TerminateMethodEntry {
        const wchar_t* methodName = nullptr;
        std::function<bool(std::uint32_t, std::string*)> invoke;
    };

    const std::vector<TerminateMethodEntry> methods{
        { L"TerminateProcess(Kernel32)", [](std::uint32_t pid, std::string* detail) { return ks::process::TerminateProcessByWin32(pid, detail); } },
        { L"NtTerminateProcess/ZwTerminateProcess", [](std::uint32_t pid, std::string* detail) { return ks::process::TerminateProcessByNtNative(pid, detail); } },
        { L"WTSTerminateProcess(WTS API)", [](std::uint32_t pid, std::string* detail) { return ks::process::TerminateProcessByWtsApi(pid, detail); } },
        { L"WinStationTerminateProcess(winsta)", [](std::uint32_t pid, std::string* detail) { return ks::process::TerminateProcessByWinStationApi(pid, detail); } },
        { L"TerminateJobObject(Job)", [](std::uint32_t pid, std::string* detail) { return ks::process::TerminateProcessByJobObject(pid, detail); } },
        { L"NtTerminateJobObject/ZwTerminateJobObject", [](std::uint32_t pid, std::string* detail) { return ks::process::TerminateProcessByNtJobObject(pid, detail); } },
        { L"RmShutdown(Restart Manager)", [](std::uint32_t pid, std::string* detail) { return ks::process::TerminateProcessByRestartManager(pid, false, detail); } },
        { L"RmShutdown(Restart Manager, force)", [](std::uint32_t pid, std::string* detail) { return ks::process::TerminateProcessByRestartManager(pid, true, detail); } },
        { L"DuplicateHandle(-1)+TerminateProcess", [](std::uint32_t pid, std::string* detail) { return ks::process::TerminateProcessByDuplicateHandlePseudo(pid, detail); } },
        { L"TerminateThread(全部线程)", [](std::uint32_t pid, std::string* detail) { return ks::process::TerminateAllThreadsByPid(pid, detail); } },
        { L"NtTerminateThread/ZwTerminateThread(全部线程)", [](std::uint32_t pid, std::string* detail) { return ks::process::TerminateAllThreadsByPidNtNative(pid, detail); } },
        { L"DebugActiveProcess 调试附加", [](std::uint32_t pid, std::string* detail) { return ks::process::TerminateProcessByDebugAttach(pid, detail); } },
        { L"ntsd -c q -p <pid>", [](std::uint32_t pid, std::string* detail) { return ks::process::TerminateProcessByNtsdCommand(pid, detail); } },
        { L"NtUnmapViewOfSection 卸载 ntdll.dll", [](std::uint32_t pid, std::string* detail) { return ks::process::TerminateProcessByNtUnmapNtdll(pid, detail); } }
    };

    for (const ProcessSnapshotRow& target : actionTargets) {
        const DWORD pid = target.processId;
        if (IsProtectedSystemPid(pid)) {
            AppendIoLine(result.detail, pid, L"组合结束", false, L"protected system PID");
            result.success = false;
            continue;
        }

        std::wstring identityError;
        Ksword::Core::UniqueHandle verifiedProcess = OpenProcessForAction(
            pid,
            target.creationTime100ns,
            PROCESS_QUERY_LIMITED_INFORMATION,
            identityError);
        if (!verifiedProcess.valid()) {
            AppendIoLine(result.detail, pid, L"组合结束", false, identityError);
            result.success = false;
            continue;
        }
        // verifiedProcess stays open through all PID-only fallback methods.

        bool queryOk = false;
        if (!IsProcessPresentBySnapshot(pid, &queryOk)) {
            AppendIoLine(result.detail, pid, L"组合结束", true, L"目标进程已不存在，无需执行结束动作。");
            continue;
        }

        std::wostringstream detail;
        detail << L"PID " << pid;
        if (!queryOk) {
            detail << L" | 初始存在性检查失败，继续执行方法链";
        }

        bool processExited = false;
        constexpr int kTerminateRoundLimit = 2;
        for (int round = 1; round <= kTerminateRoundLimit && !processExited; ++round) {
            for (const TerminateMethodEntry& method : methods) {
                std::string methodDetail;
                const bool invokeOk = method.invoke(pid, &methodDetail);
                bool postQueryOk = false;
                const bool stillPresent = IsProcessPresentBySnapshot(pid, &postQueryOk);
                detail << L"\r\n  Round " << round << L" | " << method.methodName
                       << L" | " << (invokeOk ? L"调用成功" : L"调用失败")
                       << L" | " << Utf8ToWide(methodDetail.empty() ? "无附加信息" : methodDetail.c_str());
                if (postQueryOk) {
                    detail << (stillPresent ? L" | 进程仍在运行" : L" | 已确认退出");
                } else {
                    detail << L" | 存在性检查失败，按仍在运行处理";
                }
                if (!stillPresent) {
                    processExited = true;
                    break;
                }
            }
        }

        detail << (processExited ? L"\r\n  结果：已确认退出。" : L"\r\n  结果：两轮方法链后进程仍在运行。");
        result.detail += detail.str();
        result.detail += L"\r\n";
        result.success = result.success && processExited;
    }
    return result;
}

// OpenProcessForAction opens and identity-checks one local process action handle.
// Inputs are PID, expected snapshot creation time, and desired access; processing
// keeps the matching handle alive for the caller so PID-only fallback APIs cannot
// target a later process instance. Output is an owning handle or a diagnostic.
Ksword::Core::UniqueHandle OpenProcessForAction(
    const DWORD pid,
    const ULONGLONG expectedCreationTime100ns,
    const DWORD access,
    std::wstring& errorText,
    const bool rejectProtected) {
    if (rejectProtected && IsProtectedSystemPid(pid)) {
        errorText = L"protected system PID";
        return Ksword::Core::UniqueHandle();
    }
    if (expectedCreationTime100ns == 0U) {
        errorText = L"process identity is unavailable; action skipped";
        return Ksword::Core::UniqueHandle();
    }

    const DWORD requestedAccess = access | PROCESS_QUERY_LIMITED_INFORMATION;
    HANDLE process = ::OpenProcess(requestedAccess, FALSE, pid);
    if (!process) {
        errorText = L"OpenProcess failed: " + Win32ErrorText(::GetLastError());
        return Ksword::Core::UniqueHandle();
    }

    FILETIME creationTime{};
    FILETIME exitTime{};
    FILETIME kernelTime{};
    FILETIME userTime{};
    if (!::GetProcessTimes(process, &creationTime, &exitTime, &kernelTime, &userTime)) {
        errorText = L"GetProcessTimes failed: " + Win32ErrorText(::GetLastError());
        ::CloseHandle(process);
        return Ksword::Core::UniqueHandle();
    }
    const ULONGLONG actualCreationTime100ns =
        (static_cast<ULONGLONG>(creationTime.dwHighDateTime) << 32U) |
        static_cast<ULONGLONG>(creationTime.dwLowDateTime);
    if (actualCreationTime100ns == 0U || actualCreationTime100ns != expectedCreationTime100ns) {
        errorText = L"process identity changed (PID was reused); action skipped";
        ::CloseHandle(process);
        return Ksword::Core::UniqueHandle();
    }
    return Ksword::Core::UniqueHandle(process);
}

// HoldProcessIdentityForDriverAction keeps a verified process object alive
// while a driver operation still addresses that object by PID.
bool HoldProcessIdentityForDriverAction(
    const ProcessSnapshotRow& target,
    const wchar_t* operation,
    const bool rejectProtected,
    ProcessActionResult& result,
    Ksword::Core::UniqueHandle& identityHold) {
    std::wstring identityError;
    identityHold = OpenProcessForAction(
        target.processId,
        target.creationTime100ns,
        PROCESS_QUERY_LIMITED_INFORMATION,
        identityError,
        rejectProtected);
    if (identityHold.valid()) {
        return true;
    }
    AppendIoLine(result.detail, target.processId, operation, false, identityError);
    result.success = false;
    return false;
}

// NtSuspendOrResumeProcess invokes NtSuspendProcess or NtResumeProcess for one
// PID. Inputs are PID and desired direction; processing uses ntdll dynamically;
// output is true on NT_SUCCESS and a diagnostic otherwise.
bool NtSuspendOrResumeProcess(DWORD pid, ULONGLONG expectedCreationTime100ns, bool resume, std::wstring& message) {
    const char* exportName = resume ? "NtResumeProcess" : "NtSuspendProcess";
    const FARPROC proc = NtProc(exportName);
    if (!proc) {
        message = AsciiLiteralToWide(exportName) + L" not available";
        return false;
    }

    std::wstring openError;
    Ksword::Core::UniqueHandle process = OpenProcessForAction(pid, expectedCreationTime100ns, kProcessSuspendResumeAccess, openError);
    if (!process.valid()) {
        message = openError;
        return false;
    }

    const LONG status = resume
        ? reinterpret_cast<NtResumeProcessFn>(proc)(process.get())
        : reinterpret_cast<NtSuspendProcessFn>(proc)(process.get());
    if (status >= 0) {
        message = Hex32(status);
        return true;
    }
    message = AsciiLiteralToWide(exportName) + L" failed: " + Hex32(status);
    return false;
}

// SetCriticalFlagForPid sets ProcessBreakOnTermination for one process. Inputs
// are PID and target state; processing enables SeDebugPrivilege best-effort then
// calls NtSetInformationProcess; output reports operation success.
bool SetCriticalFlagForPid(DWORD pid, ULONGLONG expectedCreationTime100ns, bool enable, std::wstring& message) {
    std::wstring privilegeDetail;
    (void)EnableCurrentProcessPrivilege(SE_DEBUG_NAME, privilegeDetail);

    const FARPROC proc = NtProc("NtSetInformationProcess");
    if (!proc) {
        message = L"NtSetInformationProcess not available";
        return false;
    }

    std::wstring openError;
    Ksword::Core::UniqueHandle process = OpenProcessForAction(pid, expectedCreationTime100ns, PROCESS_SET_INFORMATION, openError);
    if (!process.valid()) {
        message = openError;
        return false;
    }

    ULONG critical = enable ? 1UL : 0UL;
    const LONG status = reinterpret_cast<NtSetInformationProcessFn>(proc)(
        process.get(),
        kProcessBreakOnTerminationInfoClass,
        &critical,
        static_cast<ULONG>(sizeof(critical)));
    if (status >= 0) {
        message = privilegeDetail.empty() ? Hex32(status) : privilegeDetail + L"; " + Hex32(status);
        return true;
    }
    message = L"NtSetInformationProcess(ProcessBreakOnTermination) failed: " + Hex32(status);
    if (!privilegeDetail.empty()) {
        message += L"; " + privilegeDetail;
    }
    return false;
}

// SetEfficiencyModeForPid toggles Windows process power throttling. Inputs are
// PID and target state; processing calls SetProcessInformation dynamically;
// output reports operation success and a concrete Win32 diagnostic.
bool SetEfficiencyModeForPid(DWORD pid, ULONGLONG expectedCreationTime100ns, bool enable, std::wstring& message) {
    HMODULE kernel32 = ::GetModuleHandleW(L"kernel32.dll");
    const FARPROC proc = kernel32 ? ::GetProcAddress(kernel32, "SetProcessInformation") : nullptr;
    if (!proc) {
        message = L"SetProcessInformation(ProcessPowerThrottling) not available";
        return false;
    }

    std::wstring openError;
    Ksword::Core::UniqueHandle process = OpenProcessForAction(pid, expectedCreationTime100ns, PROCESS_SET_INFORMATION, openError);
    if (!process.valid()) {
        message = openError;
        return false;
    }

    ProcessPowerThrottlingStateNative powerState{};
    powerState.version = kProcessPowerThrottlingCurrentVersion;
    powerState.controlMask = kProcessPowerThrottlingExecutionSpeed;
    powerState.stateMask = enable ? kProcessPowerThrottlingExecutionSpeed : 0UL;
    const BOOL ok = reinterpret_cast<SetProcessInformationFn>(proc)(
        process.get(),
        kProcessPowerThrottlingInfoClass,
        &powerState,
        static_cast<DWORD>(sizeof(powerState)));
    if (ok) {
        message = enable ? L"Efficiency mode enabled" : L"Efficiency mode disabled";
        return true;
    }
    message = L"SetProcessInformation(ProcessPowerThrottling) failed: " + Win32ErrorText(::GetLastError());
    return false;
}

// ProtectionLevelForAction maps the menu PPL commands to the one-byte
// PS_PROTECTION level accepted by IOCTL_KSWORD_ARK_SET_PPL_LEVEL. Input is a
// menu id; output is false when the id is not a PPL command.
bool ProtectionLevelForAction(ProcessActionId actionId, std::uint8_t& levelOut) {
    switch (actionId) {
    case ProcessActionId::R0SetPplNone: levelOut = 0x00; return true;
    case ProcessActionId::R0SetPplAuthenticode: levelOut = 0x11; return true;
    case ProcessActionId::R0SetPplCodeGen: levelOut = 0x21; return true;
    case ProcessActionId::R0SetPplAntimalware: levelOut = 0x31; return true;
    case ProcessActionId::R0SetPplLsa: levelOut = 0x41; return true;
    case ProcessActionId::R0SetPplWindows: levelOut = 0x51; return true;
    case ProcessActionId::R0SetPplWinTcb: levelOut = 0x61; return true;
    default: levelOut = 0; return false;
    }
}

// IntegrityRidForAction maps the new R0 mandatory-label menu entries to the
// standard S-1-16-* RID values. Input is a context menu id; output is false when
// another action family should handle the command.
bool IntegrityRidForAction(ProcessActionId actionId, unsigned long& ridOut) {
    switch (actionId) {
    case ProcessActionId::R0SetIntegrityUntrusted: ridOut = SECURITY_MANDATORY_UNTRUSTED_RID; return true;
    case ProcessActionId::R0SetIntegrityLow: ridOut = SECURITY_MANDATORY_LOW_RID; return true;
    case ProcessActionId::R0SetIntegrityMedium: ridOut = SECURITY_MANDATORY_MEDIUM_RID; return true;
    case ProcessActionId::R0SetIntegrityMediumPlus: ridOut = SECURITY_MANDATORY_MEDIUM_PLUS_RID; return true;
    case ProcessActionId::R0SetIntegrityHigh: ridOut = SECURITY_MANDATORY_HIGH_RID; return true;
    case ProcessActionId::R0SetIntegritySystem: ridOut = SECURITY_MANDATORY_SYSTEM_RID; return true;
    default: ridOut = 0; return false;
    }
}

// IntegrityResultDetail renders the fixed response fields returned by
// DriverClient::setProcessIntegrity. Inputs are the parsed wrapper result;
// output is a compact Chinese/hex diagnostic for the context-menu status area.
std::wstring IntegrityResultDetail(const ksword::ark::ProcessIntegrityResult& io) {
    std::wostringstream stream;
    stream << Utf8ToWide(io.io.message)
           << L"; status=" << io.status
           << L"; lastStatus=" << Hex32(io.lastStatus)
           << L"; rid=0x" << std::hex << std::uppercase << io.integrityRid;
    if (io.unsupported) {
        stream << L"; unsupported";
    }
    return stream.str();
}

// InjectResultDetail renders the R0 injection response without exposing any
// reusable primitive beyond what the driver already returned. Input is the
// ArkDriverClient parsed result; output is display-only status text.
std::wstring InjectResultDetail(const ksword::ark::ProcessInjectResult& io) {
    std::wostringstream stream;
    stream << Utf8ToWide(io.io.message)
           << L"; status=" << io.status
           << L"; lastStatus=" << Hex32(io.lastStatus)
           << L"; waitStatus=" << Hex32(io.waitStatus)
           << L"; bytesWritten=" << std::dec << io.bytesWritten
           << L"; remoteBase=" << Hex64(io.remoteBaseAddress)
           << L"; entry=" << Hex64(io.entryPointAddress);
    return stream.str();
}

// ReadBinaryFileForInjection loads a selected shellcode blob into memory. Input
// is a Win32 file path; processing enforces the shared R0 payload cap before
// returning bytes; output false includes a concrete diagnostic.
bool ReadBinaryFileForInjection(
    const std::wstring& path,
    std::vector<std::uint8_t>& bytes,
    std::wstring& errorText) {
    bytes.clear();
    if (path.empty()) {
        errorText = L"shellcode file path is empty";
        return false;
    }

    std::ifstream file(path, std::ios::binary | std::ios::ate);
    if (!file) {
        errorText = L"无法打开 shellcode 文件。";
        return false;
    }
    const std::streamoff size = file.tellg();
    if (size <= 0) {
        errorText = L"shellcode 文件为空。";
        return false;
    }
    if (static_cast<unsigned long long>(size) > KSWORD_ARK_PROCESS_INJECT_MAX_PAYLOAD_BYTES) {
        errorText = L"shellcode 文件超过 R0 注入协议上限 " +
            std::to_wstring(KSWORD_ARK_PROCESS_INJECT_MAX_PAYLOAD_BYTES) + L" 字节。";
        return false;
    }

    bytes.resize(static_cast<std::size_t>(size));
    file.seekg(0, std::ios::beg);
    if (!file.read(reinterpret_cast<char*>(bytes.data()), size)) {
        errorText = L"读取 shellcode 文件失败。";
        bytes.clear();
        return false;
    }
    return true;
}

// VisibilityRequestForAction maps R0 process-hide menu ids onto the shared
// KswordArkProcessIoctl visibility action/flag contract. Input is a menu id;
// output is false when another action family should handle the command.
bool VisibilityRequestForAction(ProcessActionId actionId, unsigned long& actionOut, unsigned long& flagsOut) {
    switch (actionId) {
    case ProcessActionId::R0HideUnlinkOnly:
        actionOut = KSWORD_ARK_PROCESS_VISIBILITY_ACTION_HIDE;
        flagsOut = KSWORD_ARK_PROCESS_VISIBILITY_FLAG_UNLINK_ACTIVE_LIST;
        return true;
    case ProcessActionId::R0HidePatchPidOnly:
        actionOut = KSWORD_ARK_PROCESS_VISIBILITY_ACTION_HIDE;
        flagsOut = KSWORD_ARK_PROCESS_VISIBILITY_FLAG_PATCH_UNIQUE_PID;
        return true;
    case ProcessActionId::R0HideLegacyBoth:
        actionOut = KSWORD_ARK_PROCESS_VISIBILITY_ACTION_HIDE;
        flagsOut = KSWORD_ARK_PROCESS_VISIBILITY_FLAG_LEGACY_BOTH;
        return true;
    case ProcessActionId::R0UnhideProcess:
        actionOut = KSWORD_ARK_PROCESS_VISIBILITY_ACTION_UNHIDE;
        flagsOut = 0;
        return true;
    case ProcessActionId::R0ClearHiddenMarks:
        actionOut = KSWORD_ARK_PROCESS_VISIBILITY_ACTION_CLEAR_ALL;
        flagsOut = 0;
        return true;
    default:
        actionOut = 0;
        flagsOut = 0;
        return false;
    }
}

// SpecialProcessActionForMenu maps BreakOnTermination/APC menu entries onto
// IOCTL_KSWORD_ARK_SET_PROCESS_SPECIAL_FLAGS action values. Input is a menu id;
// output is false when the command belongs to another operation family.
bool SpecialProcessActionForMenu(ProcessActionId actionId, unsigned long& actionOut) {
    switch (actionId) {
    case ProcessActionId::R0EnableBreakOnTermination:
        actionOut = KSWORD_ARK_PROCESS_SPECIAL_ACTION_ENABLE_BREAK_ON_TERMINATION;
        return true;
    case ProcessActionId::R0DisableBreakOnTermination:
        actionOut = KSWORD_ARK_PROCESS_SPECIAL_ACTION_DISABLE_BREAK_ON_TERMINATION;
        return true;
    case ProcessActionId::R0DisableApcInsertion:
        actionOut = KSWORD_ARK_PROCESS_SPECIAL_ACTION_DISABLE_APC_INSERTION;
        return true;
    default:
        actionOut = 0;
        return false;
    }
}

bool SetPriorityForPid(
    DWORD pid,
    ULONGLONG expectedCreationTime100ns,
    DWORD priorityClass,
    std::wstring& detail) {
    std::wstring openError;
    Ksword::Core::UniqueHandle process = OpenProcessForAction(
        pid,
        expectedCreationTime100ns,
        PROCESS_SET_INFORMATION,
        openError);
    if (!process.valid()) {
        detail += L"PID " + std::to_wstring(pid) + L": " + openError + L"\r\n";
        return false;
    }
    const BOOL ok = ::SetPriorityClass(process.get(), priorityClass);
    const DWORD error = ok ? ERROR_SUCCESS : ::GetLastError();
    detail += L"PID " + std::to_wstring(pid) + (ok ? L": SetPriorityClass OK" : L": SetPriorityClass failed ") +
        (ok ? L"" : std::to_wstring(error)) + L"\r\n";
    return ok != FALSE;
}

// KeyboardEnumOk checks shared keyboard enumeration status values. Input is the
// R0 aggregate status; output accepts OK and PARTIAL because partial still gives
// usable rows for the UI.
bool KeyboardEnumOk(std::uint32_t status) {
    return status == KSWORD_ARK_KEYBOARD_ENUM_STATUS_OK ||
        status == KSWORD_ARK_KEYBOARD_ENUM_STATUS_PARTIAL;
}

// ExecuteKeyboardHotkeyScan queries R0 win32k hotkey/hook evidence for one
// process. Inputs are one captured process row; processing uses ArkDriverClient only;
// output is a ProcessActionResult suitable for the context menu status dialog.
ProcessActionResult ExecuteKeyboardHotkeyScan(const ProcessSnapshotRow& target) {
    ProcessActionResult result;
    result.title = L"扫描进程热键";
    const DWORD pid = target.processId;
    Ksword::Core::UniqueHandle identityHold;
    if (!HoldProcessIdentityForDriverAction(target, L"scan hotkeys", false, result, identityHold)) {
        return result;
    }
    const ksword::ark::DriverClient driverClient;

    const ksword::ark::KeyboardHotkeyEnumResult hotkeys = driverClient.enumerateKeyboardHotkeys(
        static_cast<std::uint32_t>(pid),
        KSWORD_ARK_KEYBOARD_ENUM_FLAG_FILTER_PROCESS |
            KSWORD_ARK_KEYBOARD_ENUM_FLAG_INCLUDE_SYSTEM |
            KSWORD_ARK_KEYBOARD_ENUM_FLAG_INCLUDE_DIAGNOSTICS,
        2048UL);
    const ksword::ark::KeyboardHookEnumResult hooks = driverClient.enumerateKeyboardHooks(
        static_cast<std::uint32_t>(pid),
        KSWORD_ARK_KEYBOARD_ENUM_FLAG_FILTER_PROCESS |
            KSWORD_ARK_KEYBOARD_ENUM_FLAG_INCLUDE_THREAD_HOOKS |
            KSWORD_ARK_KEYBOARD_ENUM_FLAG_INCLUDE_GLOBAL_HOOKS |
            KSWORD_ARK_KEYBOARD_ENUM_FLAG_INCLUDE_DIAGNOSTICS,
        2048UL);

    const bool hotkeyOk = hotkeys.io.ok && KeyboardEnumOk(hotkeys.status);
    const bool hookOk = hooks.io.ok && KeyboardEnumOk(hooks.status);
    result.success = hotkeyOk || hookOk;

    std::wostringstream detail;
    detail << L"PID " << pid << L"\r\n"
           << L"Hotkeys: IO=" << (hotkeys.io.ok ? L"OK" : L"FAIL")
           << L", status=" << hotkeys.status
           << L", total=" << hotkeys.totalCount
           << L", returned=" << hotkeys.returnedCount
           << L", parsed=" << hotkeys.entries.size()
           << L", message=" << Utf8ToWide(hotkeys.io.message) << L"\r\n"
           << L"Hooks: IO=" << (hooks.io.ok ? L"OK" : L"FAIL")
           << L", status=" << hooks.status
           << L", total=" << hooks.totalCount
           << L", returned=" << hooks.returnedCount
           << L", parsed=" << hooks.entries.size()
           << L", message=" << Utf8ToWide(hooks.io.message) << L"\r\n";

    const std::size_t hotkeyLimit = std::min<std::size_t>(hotkeys.entries.size(), 16U);
    for (std::size_t i = 0; i < hotkeyLimit; ++i) {
        const ksword::ark::KeyboardHotkeyEntry& row = hotkeys.entries[i];
        detail << L"Hotkey[" << i << L"] vk=0x" << std::hex << std::uppercase << row.virtualKey
               << L", modifiers=0x" << row.modifiers
               << L", tid=" << std::dec << row.threadId
               << L", object=" << Hex64(row.hotkeyObject)
               << L", detail=" << row.detail << L"\r\n";
    }

    const std::size_t hookLimit = std::min<std::size_t>(hooks.entries.size(), 16U);
    for (std::size_t i = 0; i < hookLimit; ++i) {
        const ksword::ark::KeyboardHookEntry& row = hooks.entries[i];
        detail << L"Hook[" << i << L"] type=" << row.hookType
               << L", scope=" << row.hookScope
               << L", tid=" << row.threadId
               << L", proc=" << Hex64(row.procedureAddress)
               << L", detail=" << row.detail << std::dec << L"\r\n";
    }

    result.detail = detail.str();
    return result;
}

// ExecuteLocalProcessAction applies one local Win32/NtAPI action to captured
// process instances. Each helper validates the snapshot creation time on the
// same handle used by its mutation, so a reused PID is rejected.
ProcessActionResult ExecuteLocalProcessAction(
    ProcessActionId actionId,
    const std::vector<ProcessSnapshotRow>& actionTargets) {
    ProcessActionResult result;
    result.success = true;
    bool handled = true;
    const wchar_t* operation = L"";
    switch (actionId) {
    case ProcessActionId::SuspendProcess:
        result.title = L"挂起进程";
        operation = L"NtSuspendProcess";
        break;
    case ProcessActionId::ResumeProcess:
        result.title = L"恢复进程";
        operation = L"NtResumeProcess";
        break;
    case ProcessActionId::EnableEfficiencyMode:
        result.title = L"开启效率模式";
        operation = L"Efficiency on";
        break;
    case ProcessActionId::DisableEfficiencyMode:
        result.title = L"关闭效率模式";
        operation = L"Efficiency off";
        break;
    case ProcessActionId::SetCriticalProcess:
        result.title = L"设为关键进程";
        operation = L"Critical on";
        break;
    case ProcessActionId::ClearCriticalProcess:
        result.title = L"取消关键进程";
        operation = L"Critical off";
        break;
    default:
        handled = false;
        break;
    }

    if (!handled) {
        result.success = false;
        result.title = L"进程动作";
        result.detail = L"未知本地进程动作。";
        return result;
    }

    for (const ProcessSnapshotRow& target : actionTargets) {
        const DWORD pid = target.processId;
        const ULONGLONG expectedCreationTime100ns = target.creationTime100ns;
        std::wstring message;
        bool ok = false;
        switch (actionId) {
        case ProcessActionId::SuspendProcess:
            ok = NtSuspendOrResumeProcess(pid, expectedCreationTime100ns, false, message);
            break;
        case ProcessActionId::ResumeProcess:
            ok = NtSuspendOrResumeProcess(pid, expectedCreationTime100ns, true, message);
            break;
        case ProcessActionId::EnableEfficiencyMode:
            ok = SetEfficiencyModeForPid(pid, expectedCreationTime100ns, true, message);
            break;
        case ProcessActionId::DisableEfficiencyMode:
            ok = SetEfficiencyModeForPid(pid, expectedCreationTime100ns, false, message);
            break;
        case ProcessActionId::SetCriticalProcess:
            ok = SetCriticalFlagForPid(pid, expectedCreationTime100ns, true, message);
            break;
        case ProcessActionId::ClearCriticalProcess:
            ok = SetCriticalFlagForPid(pid, expectedCreationTime100ns, false, message);
            break;
        default:
            message = L"unknown action";
            ok = false;
            break;
        }
        AppendIoLine(result.detail, pid, operation, ok, message);
        result.success = result.success && ok;
    }
    return result;
}
// ExecutePplRefresh queries the R0 process enumeration table and extracts the
// selected snapshot instances' protection bytes. Each verified handle remains
// live through the shared driver query so a recycled PID cannot supply results.
ProcessActionResult ExecutePplRefresh(const std::vector<ProcessSnapshotRow>& actionTargets) {
    ProcessActionResult result;
    result.title = L"手动刷新PPL保护级别";
    result.success = true;

    std::vector<ProcessSnapshotRow> verifiedTargets;
    std::vector<Ksword::Core::UniqueHandle> identityHolds;
    verifiedTargets.reserve(actionTargets.size());
    identityHolds.reserve(actionTargets.size());
    for (const ProcessSnapshotRow& target : actionTargets) {
        Ksword::Core::UniqueHandle identityHold;
        if (!HoldProcessIdentityForDriverAction(target, L"PPL refresh", false, result, identityHold)) {
            continue;
        }
        verifiedTargets.push_back(target);
        identityHolds.push_back(std::move(identityHold));
    }
    if (verifiedTargets.empty()) {
        return result;
    }

    const ksword::ark::DriverClient driverClient;
    const ksword::ark::ProcessEnumResult query = driverClient.enumerateProcesses(0);
    if (!query.io.ok) {
        result.success = false;
        result.detail = L"R0 process enumeration failed: " + Utf8ToWide(query.io.message);
        return result;
    }

    for (const ProcessSnapshotRow& target : verifiedTargets) {
        const DWORD pid = target.processId;
        const auto it = std::find_if(query.entries.begin(), query.entries.end(), [pid](const ksword::ark::ProcessEntry& row) {
            return row.processId == static_cast<std::uint32_t>(pid);
        });
        if (it == query.entries.end()) {
            AppendIoLine(result.detail, pid, L"PPL refresh", false, L"PID not returned by R0 enumeration");
            result.success = false;
            continue;
        }

        std::wostringstream line;
        line << L"protection=0x" << std::hex << std::uppercase << static_cast<unsigned int>(it->protection)
             << L", signature=0x" << static_cast<unsigned int>(it->signatureLevel)
             << L", sectionSignature=0x" << static_cast<unsigned int>(it->sectionSignatureLevel)
             << L", fieldFlags=0x" << it->fieldFlags
             << L", r0Status=" << std::dec << it->r0Status;
        const bool hasProtection = (it->fieldFlags & KSWORD_ARK_PROCESS_FIELD_PROTECTION_PRESENT) != 0;
        AppendIoLine(result.detail, pid, L"PPL refresh", hasProtection, line.str());
        result.success = result.success && hasProtection;
    }
    return result;
}
} // namespace

ProcessActionResult ExecuteProcessAction(
    ProcessActionId actionId,
    const std::vector<DWORD>& selectedPids,
    const std::vector<ProcessSnapshotRow>& snapshotRows) {
    if (selectedPids.empty() && actionId != ProcessActionId::R0ClearHiddenMarks) {
        return FailureResult(L"进程动作", selectedPids, L"没有选中进程。");
    }

    const auto buildActionTargets = [&snapshotRows](const std::vector<DWORD>& pids) {
        return BuildProcessActionTargets(pids, snapshotRows);
    };

    const DWORD priorityClass = PriorityClassForAction(actionId);
    if (priorityClass != 0) {
        ProcessActionResult result;
        result.title = L"设置进程优先级";
        result.success = true;
        for (const ProcessSnapshotRow& target : buildActionTargets(selectedPids)) {
            if (!SetPriorityForPid(
                    target.processId,
                    target.creationTime100ns,
                    priorityClass,
                    result.detail)) {
                result.success = false;
            }
        }
        return result;
    }

    if (actionId == ProcessActionId::OpenFolder) {
        const ProcessSnapshotRow* row = FindRowByPid(snapshotRows, selectedPids.front());
        if (!row || row->imagePath.empty()) {
            return FailureResult(L"打开所在目录", selectedPids, L"选中进程的映像路径不可用。");
        }
        const std::wstring args = L"/select,\"" + row->imagePath + L"\"";
        const HINSTANCE shellResult = ::ShellExecuteW(nullptr, L"open", L"explorer.exe", args.c_str(), nullptr, SW_SHOWNORMAL);
        ProcessActionResult result;
        result.title = L"打开所在目录";
        result.success = reinterpret_cast<INT_PTR>(shellResult) > 32;
        result.detail = result.success ? L"Explorer launch requested." : L"ShellExecuteW failed.";
        return result;
    }

    if (actionId == ProcessActionId::TerminateProcessMultiMethod) {
        return ExecuteMultiMethodTerminate(buildActionTargets(selectedPids));
    }

    if (actionId == ProcessActionId::TerminateProcessTree) {
        const std::vector<DWORD> treePids = CollectR3ProcessTreePids(selectedPids, snapshotRows);
        if (treePids.empty()) {
            return FailureResult(L"结束进程树", selectedPids, L"选中进程未包含在当前 R3 进程快照中，无法识别进程树。");
        }
        ProcessActionResult result = ExecuteMultiMethodTerminate(buildActionTargets(treePids));
        result.title = L"结束进程树";
        return result;
    }

    if (actionId == ProcessActionId::TerminateProcess) {
        ProcessActionResult result;
        result.title = L"结束进程";
        result.success = true;
        for (const ProcessSnapshotRow& target : buildActionTargets(selectedPids)) {
            const DWORD pid = target.processId;
            if (IsProtectedSystemPid(pid)) {
                AppendIoLine(result.detail, pid, L"TerminateProcess", false, L"protected system PID");
                result.success = false;
                continue;
            }
            std::wstring openError;
            Ksword::Core::UniqueHandle process = OpenProcessForAction(
                pid,
                target.creationTime100ns,
                PROCESS_TERMINATE,
                openError);
            if (!process.valid()) {
                AppendIoLine(result.detail, pid, L"TerminateProcess", false, openError);
                result.success = false;
                continue;
            }
            const BOOL ok = ::TerminateProcess(process.get(), static_cast<UINT>(0xC0000005u));
            const DWORD error = ok ? ERROR_SUCCESS : ::GetLastError();
            AppendIoLine(result.detail, pid, L"TerminateProcess", ok != FALSE, ok ? L"" : L"Win32 error " + std::to_wstring(error));
            result.success = result.success && ok != FALSE;
        }
        return result;
    }

    if (actionId == ProcessActionId::R0TerminateProcess || actionId == ProcessActionId::R0TerminateProcessTree) {
        const bool terminateTree = actionId == ProcessActionId::R0TerminateProcessTree;
        const std::vector<DWORD> targetPids = terminateTree
            ? CollectR3ProcessTreePids(selectedPids, snapshotRows)
            : selectedPids;
        if (targetPids.empty()) {
            return FailureResult(L"R0结束进程树", selectedPids, L"选中进程未包含在当前 R3 进程快照中，无法识别进程树。");
        }
        ProcessActionResult result;
        result.title = terminateTree ? L"R0结束进程树" : L"R0结束进程";
        result.success = true;
        const ksword::ark::DriverClient driverClient;
        for (const ProcessSnapshotRow& target : buildActionTargets(targetPids)) {
            const DWORD pid = target.processId;
            if (IsProtectedSystemPid(pid)) {
                AppendIoLine(result.detail, pid, L"R0 terminate", false, L"protected system PID");
                result.success = false;
                continue;
            }
            Ksword::Core::UniqueHandle identityHold;
            if (!HoldProcessIdentityForDriverAction(target, L"R0 terminate", true, result, identityHold)) {
                continue;
            }
            // 每个 PID 独立调用 ArkDriverClient，因此会单独提交现有结束进程 IOCTL。
            const ksword::ark::IoResult io = driverClient.terminateProcess(static_cast<std::uint32_t>(pid), static_cast<long>(0xC0000005u));
            AppendIoLine(result.detail, pid, L"R0 terminate", io.ok, Utf8ToWide(io.message));
            result.success = result.success && io.ok;
        }
        return result;
    }

    if (actionId == ProcessActionId::R0SuspendProcess) {
        ProcessActionResult result;
        result.title = L"R0挂起进程";
        result.success = true;
        const ksword::ark::DriverClient driverClient;
        for (const ProcessSnapshotRow& target : buildActionTargets(selectedPids)) {
            const DWORD pid = target.processId;
            if (IsProtectedSystemPid(pid)) {
                AppendIoLine(result.detail, pid, L"R0 suspend", false, L"protected system PID");
                result.success = false;
                continue;
            }
            Ksword::Core::UniqueHandle identityHold;
            if (!HoldProcessIdentityForDriverAction(target, L"R0 suspend", true, result, identityHold)) {
                continue;
            }
            const ksword::ark::IoResult io = driverClient.suspendProcess(static_cast<std::uint32_t>(pid));
            AppendIoLine(result.detail, pid, L"R0 suspend", io.ok, Utf8ToWide(io.message));
            result.success = result.success && io.ok;
        }
        return result;
    }

    std::uint8_t protectionLevel = 0;
    if (ProtectionLevelForAction(actionId, protectionLevel)) {
        ProcessActionResult result;
        result.title = L"R0设置PPL层级";
        result.success = true;
        const ksword::ark::DriverClient driverClient;
        for (const ProcessSnapshotRow& target : buildActionTargets(selectedPids)) {
            const DWORD pid = target.processId;
            if (IsProtectedSystemPid(pid)) {
                AppendIoLine(result.detail, pid, L"set PPL", false, L"protected system PID");
                result.success = false;
                continue;
            }
            Ksword::Core::UniqueHandle identityHold;
            if (!HoldProcessIdentityForDriverAction(target, L"set PPL", true, result, identityHold)) {
                continue;
            }
            const ksword::ark::IoResult io = driverClient.setProcessProtection(static_cast<std::uint32_t>(pid), protectionLevel);
            AppendIoLine(result.detail, pid, L"set PPL", io.ok, Utf8ToWide(io.message));
            result.success = result.success && io.ok;
        }
        return result;
    }

    unsigned long integrityRid = 0;
    if (IntegrityRidForAction(actionId, integrityRid)) {
        ProcessActionResult result;
        result.title = L"R0设置进程完整性";
        result.success = true;
        const ksword::ark::DriverClient driverClient;
        for (const ProcessSnapshotRow& target : buildActionTargets(selectedPids)) {
            const DWORD pid = target.processId;
            if (IsProtectedSystemPid(pid)) {
                AppendIoLine(result.detail, pid, L"set integrity", false, L"protected system PID");
                result.success = false;
                continue;
            }
            Ksword::Core::UniqueHandle identityHold;
            if (!HoldProcessIdentityForDriverAction(target, L"set integrity", true, result, identityHold)) {
                continue;
            }
            const ksword::ark::ProcessIntegrityResult io =
                driverClient.setProcessIntegrity(static_cast<std::uint32_t>(pid), integrityRid);
            const bool ok = io.io.ok &&
                !io.unsupported &&
                io.lastStatus >= 0 &&
                io.status == KSWORD_ARK_PROCESS_INTEGRITY_STATUS_APPLIED;
            AppendIoLine(result.detail, pid, L"set integrity", ok, IntegrityResultDetail(io));
            result.success = result.success && ok;
        }
        return result;
    }

    unsigned long visibilityAction = 0;
    unsigned long visibilityFlags = 0;
    if (VisibilityRequestForAction(actionId, visibilityAction, visibilityFlags)) {
        ProcessActionResult result;
        result.title = L"R0进程可见性";
        result.success = true;
        const ksword::ark::DriverClient driverClient;
        if (actionId == ProcessActionId::R0ClearHiddenMarks) {
            const ksword::ark::ProcessVisibilityResult io = driverClient.setProcessVisibility(0, visibilityAction, visibilityFlags);
            const bool ok = io.io.ok && io.lastStatus >= 0 && io.status == KSWORD_ARK_PROCESS_VISIBILITY_STATUS_CLEARED;
            AppendIoLine(result.detail, L"R0 clear hidden marks", ok, Utf8ToWide(io.io.message));
            result.success = ok;
            return result;
        }
        for (const ProcessSnapshotRow& target : buildActionTargets(selectedPids)) {
            const DWORD pid = target.processId;
            if (visibilityAction == KSWORD_ARK_PROCESS_VISIBILITY_ACTION_HIDE && IsProtectedSystemPid(pid)) {
                AppendIoLine(result.detail, pid, L"visibility", false, L"protected system PID");
                result.success = false;
                continue;
            }
            Ksword::Core::UniqueHandle identityHold;
            if (!HoldProcessIdentityForDriverAction(
                    target,
                    L"visibility",
                    visibilityAction == KSWORD_ARK_PROCESS_VISIBILITY_ACTION_HIDE,
                    result,
                    identityHold)) {
                continue;
            }
            const ksword::ark::ProcessVisibilityResult io = driverClient.setProcessVisibility(static_cast<std::uint32_t>(pid), visibilityAction, visibilityFlags);
            const bool ok = io.io.ok && io.lastStatus >= 0 &&
                (io.status == KSWORD_ARK_PROCESS_VISIBILITY_STATUS_HIDDEN ||
                 io.status == KSWORD_ARK_PROCESS_VISIBILITY_STATUS_VISIBLE ||
                 io.status == KSWORD_ARK_PROCESS_VISIBILITY_STATUS_CLEARED);
            AppendIoLine(result.detail, pid, L"visibility", ok, Utf8ToWide(io.io.message));
            result.success = result.success && ok;
        }
        return result;
    }

    unsigned long specialAction = 0;
    if (SpecialProcessActionForMenu(actionId, specialAction)) {
        ProcessActionResult result;
        result.title = L"R0进程特殊标志";
        result.success = true;
        const ksword::ark::DriverClient driverClient;
        for (const ProcessSnapshotRow& target : buildActionTargets(selectedPids)) {
            const DWORD pid = target.processId;
            if (IsProtectedSystemPid(pid)) {
                AppendIoLine(result.detail, pid, L"special flags", false, L"protected system PID");
                result.success = false;
                continue;
            }
            Ksword::Core::UniqueHandle identityHold;
            if (!HoldProcessIdentityForDriverAction(target, L"special flags", true, result, identityHold)) {
                continue;
            }
            const ksword::ark::ProcessSpecialFlagsResult io = driverClient.setProcessSpecialFlags(static_cast<std::uint32_t>(pid), specialAction);
            const bool ok = io.io.ok && io.lastStatus >= 0 && io.status == KSWORD_ARK_PROCESS_SPECIAL_STATUS_APPLIED;
            AppendIoLine(result.detail, pid, L"special flags", ok, Utf8ToWide(io.io.message));
            result.success = result.success && ok;
        }
        return result;
    }

    if (actionId == ProcessActionId::R0DkomRemoveFromCidTable) {
        ProcessActionResult result;
        result.title = L"R0 DKOM从PspCidTable删除";
        result.success = true;
        const ksword::ark::DriverClient driverClient;
        for (const ProcessSnapshotRow& target : buildActionTargets(selectedPids)) {
            const DWORD pid = target.processId;
            if (IsProtectedSystemPid(pid)) {
                AppendIoLine(result.detail, pid, L"DKOM CID remove", false, L"protected system PID");
                result.success = false;
                continue;
            }
            Ksword::Core::UniqueHandle identityHold;
            if (!HoldProcessIdentityForDriverAction(target, L"DKOM CID remove", true, result, identityHold)) {
                continue;
            }
            const ksword::ark::ProcessDkomResult io = driverClient.dkomProcess(static_cast<std::uint32_t>(pid), KSWORD_ARK_PROCESS_DKOM_ACTION_REMOVE_FROM_PSP_CID_TABLE);
            const bool ok = io.io.ok && io.lastStatus >= 0 && io.status == KSWORD_ARK_PROCESS_DKOM_STATUS_REMOVED && io.removedEntries > 0;
            AppendIoLine(result.detail, pid, L"DKOM CID remove", ok, Utf8ToWide(io.io.message));
            result.success = result.success && ok;
        }
        return result;
    }

    switch (actionId) {
    case ProcessActionId::SuspendProcess:
    case ProcessActionId::ResumeProcess:
    case ProcessActionId::EnableEfficiencyMode:
    case ProcessActionId::DisableEfficiencyMode:
    case ProcessActionId::SetCriticalProcess:
    case ProcessActionId::ClearCriticalProcess:
        return ExecuteLocalProcessAction(actionId, buildActionTargets(selectedPids));
    case ProcessActionId::RefreshPplProtectionLevel:
        return ExecutePplRefresh(buildActionTargets(selectedPids));
    case ProcessActionId::OpenMemoryOperation: {
        ProcessActionResult result;
        result.title = L"复制到内存读写页输入";
        const std::wstring pidText = PidListText(selectedPids);
        result.success = WriteClipboardText(nullptr, pidText);
        result.detail = result.success
            ? L"已复制 PID 列表，可粘贴到驱动内存读写页的目标 PID 输入框: " + pidText
            : L"复制 PID 列表到剪贴板失败: " + Win32ErrorText(::GetLastError());
        return result;
    }
    case ProcessActionId::ScanHotkeys:
        if (selectedPids.size() != 1) {
            return FailureResult(L"扫描进程热键", selectedPids, L"扫描进程热键需要单选一个进程。");
        }
        {
            const std::vector<ProcessSnapshotRow> actionTargets = buildActionTargets(selectedPids);
            if (actionTargets.size() != 1U) {
                return FailureResult(L"扫描进程热键", selectedPids, L"扫描进程热键需要单选一个进程。");
            }
            return ExecuteKeyboardHotkeyScan(actionTargets.front());
        }
    case ProcessActionId::OpenDetails:
        return FailureResult(L"进程详细信息", selectedPids, L"该动作由进程列表窗口直接打开详细信息页。");
    default:
        return FailureResult(L"进程动作", selectedPids, L"未知进程动作。");
    }
}

ProcessActionResult ExecuteR0ProcessDllInjection(
    const std::vector<DWORD>& selectedPids,
    const std::vector<ProcessSnapshotRow>& snapshotRows,
    const std::wstring& dllPath) {
    if (selectedPids.size() != 1) {
        return FailureResult(L"R0 DLL注入", selectedPids, L"R0 DLL 注入需要单选一个进程。");
    }
    if (dllPath.empty()) {
        return FailureResult(L"R0 DLL注入", selectedPids, L"未选择 DLL 文件。");
    }

    const std::vector<ProcessSnapshotRow> actionTargets = BuildProcessActionTargets(selectedPids, snapshotRows);
    if (actionTargets.size() != 1U) {
        return FailureResult(L"R0 DLL注入", selectedPids, L"R0 DLL 注入需要单选一个进程。");
    }

    ProcessActionResult result;
    result.title = L"R0 DLL注入";
    const ProcessSnapshotRow& target = actionTargets.front();
    const DWORD pid = target.processId;
    if (IsProtectedSystemPid(pid)) {
        result.success = false;
        AppendIoLine(result.detail, pid, L"inject DLL", false, L"protected system PID");
        return result;
    }
    Ksword::Core::UniqueHandle identityHold;
    if (!HoldProcessIdentityForDriverAction(target, L"inject DLL", true, result, identityHold)) {
        return result;
    }

    const ksword::ark::DriverClient driverClient;
    const ksword::ark::ProcessInjectResult io = driverClient.injectProcessDll(
        static_cast<std::uint32_t>(pid),
        dllPath,
        KSWORD_ARK_PROCESS_INJECT_FLAG_UI_CONFIRMED | KSWORD_ARK_PROCESS_INJECT_FLAG_WAIT_THREAD);
    result.success = io.io.ok &&
        io.lastStatus >= 0 &&
        io.status == KSWORD_ARK_PROCESS_INJECT_STATUS_INJECTED;
    AppendIoLine(result.detail, pid, L"inject DLL", result.success, InjectResultDetail(io));
    return result;
}

ProcessActionResult ExecuteR0ProcessShellcodeInjection(
    const std::vector<DWORD>& selectedPids,
    const std::vector<ProcessSnapshotRow>& snapshotRows,
    const std::wstring& shellcodePath) {
    if (selectedPids.size() != 1) {
        return FailureResult(L"R0 Shellcode注入", selectedPids, L"R0 Shellcode 注入需要单选一个进程。");
    }

    std::vector<std::uint8_t> shellcode;
    std::wstring readError;
    if (!ReadBinaryFileForInjection(shellcodePath, shellcode, readError)) {
        return FailureResult(L"R0 Shellcode注入", selectedPids, readError.c_str());
    }

    const std::vector<ProcessSnapshotRow> actionTargets = BuildProcessActionTargets(selectedPids, snapshotRows);
    if (actionTargets.size() != 1U) {
        return FailureResult(L"R0 Shellcode注入", selectedPids, L"R0 Shellcode 注入需要单选一个进程。");
    }

    ProcessActionResult result;
    result.title = L"R0 Shellcode注入";
    const ProcessSnapshotRow& target = actionTargets.front();
    const DWORD pid = target.processId;
    if (IsProtectedSystemPid(pid)) {
        result.success = false;
        AppendIoLine(result.detail, pid, L"inject shellcode", false, L"protected system PID");
        return result;
    }
    Ksword::Core::UniqueHandle identityHold;
    if (!HoldProcessIdentityForDriverAction(target, L"inject shellcode", true, result, identityHold)) {
        return result;
    }

    const ksword::ark::DriverClient driverClient;
    const ksword::ark::ProcessInjectResult io = driverClient.injectProcessShellcode(
        static_cast<std::uint32_t>(pid),
        shellcode,
        KSWORD_ARK_PROCESS_INJECT_FLAG_UI_CONFIRMED);
    result.success = io.io.ok &&
        io.lastStatus >= 0 &&
        io.status == KSWORD_ARK_PROCESS_INJECT_STATUS_INJECTED;
    AppendIoLine(result.detail, pid, L"inject shellcode", result.success, InjectResultDetail(io));
    return result;
}

DWORD PriorityClassForAction(ProcessActionId actionId) {
    switch (actionId) {
    case ProcessActionId::SetPriorityIdle: return IDLE_PRIORITY_CLASS;
    case ProcessActionId::SetPriorityBelowNormal: return BELOW_NORMAL_PRIORITY_CLASS;
    case ProcessActionId::SetPriorityNormal: return NORMAL_PRIORITY_CLASS;
    case ProcessActionId::SetPriorityAboveNormal: return ABOVE_NORMAL_PRIORITY_CLASS;
    case ProcessActionId::SetPriorityHigh: return HIGH_PRIORITY_CLASS;
    case ProcessActionId::SetPriorityRealtime: return REALTIME_PRIORITY_CLASS;
    default: return 0;
    }
}

} // namespace Ksword::Features::Process
